@unvired/react-native-unvired-sdk 0.0.15 → 0.0.16
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 +1 -1
- package/dist/database/DatabaseManager.js +1 -1
- package/dist/database/services/DatabaseNative.d.ts +0 -1
- package/dist/database/services/DatabaseNative.js +35 -66
- package/dist/file-system/FileSystem.d.ts +1 -1
- package/dist/file-system/FileSystem.js +1 -1
- package/dist/file-system/RNFileEntry.d.ts +39 -10
- package/dist/file-system/RNFileEntry.js +152 -87
- package/dist/file-system/services/FileSystemNative.d.ts +3 -3
- package/dist/file-system/services/FileSystemNative.js +38 -21
- package/dist/logger/services/LoggerNative.js +3 -3
- package/dist/logger/services/LoggerWindows.js +3 -3
- package/package.json +20 -12
- package/ts-core/PlatformInterface.ts +109 -0
- package/ts-core/ReactNativeFileEntry.ts +208 -0
- package/{example/USAGE_EXAMPLE.ts → ts-core/ReactNativePlatformAdapter.ts} +10 -9
package/BuildNo.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
R-0.000.
|
|
1
|
+
R-0.000.0016
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IDatabaseAdapter } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* DatabaseManager - Manages database operations across platforms
|
|
4
|
-
* - iOS/Android/Windows: Uses
|
|
4
|
+
* - iOS/Android/Windows: Uses @op-engineering/op-sqlite
|
|
5
5
|
* - Web: Uses WebSQL
|
|
6
6
|
*/
|
|
7
7
|
export declare class DatabaseManager {
|
|
@@ -5,7 +5,7 @@ import { DatabaseWeb } from './services/DatabaseWeb';
|
|
|
5
5
|
const dbImpl = Platform.OS === 'web' ? new DatabaseWeb() : new DatabaseNative();
|
|
6
6
|
/**
|
|
7
7
|
* DatabaseManager - Manages database operations across platforms
|
|
8
|
-
* - iOS/Android/Windows: Uses
|
|
8
|
+
* - iOS/Android/Windows: Uses @op-engineering/op-sqlite
|
|
9
9
|
* - Web: Uses WebSQL
|
|
10
10
|
*/
|
|
11
11
|
export class DatabaseManager {
|
|
@@ -1,33 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
SQLite.enablePromise(true);
|
|
3
|
-
SQLite.DEBUG(false);
|
|
1
|
+
import { open } from '@op-engineering/op-sqlite';
|
|
4
2
|
export class DatabaseNative {
|
|
5
3
|
constructor() {
|
|
6
4
|
this.databases = new Map();
|
|
7
5
|
this.defaultLocation = "default";
|
|
8
6
|
}
|
|
9
|
-
|
|
7
|
+
getDatabase(name, location) {
|
|
10
8
|
const dbKey = `${name}_${location || this.defaultLocation}`;
|
|
11
9
|
if (this.databases.has(dbKey)) {
|
|
12
10
|
return this.databases.get(dbKey);
|
|
13
11
|
}
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
// @op-engineering/op-sqlite open function
|
|
13
|
+
// We treat location as mainly advisory since op-sqlite defaults to valid paths.
|
|
14
|
+
// If strict paths are needed, we would need to construct absolute path using FS.
|
|
15
|
+
// For now, we rely on the name.
|
|
16
|
+
const db = open({ name });
|
|
16
17
|
this.databases.set(dbKey, db);
|
|
17
18
|
return db;
|
|
18
19
|
}
|
|
19
|
-
resultSetToArray(resultSet) {
|
|
20
|
-
const results = [];
|
|
21
|
-
for (let i = 0; i < resultSet.rows.length; i++) {
|
|
22
|
-
results.push(resultSet.rows.item(i));
|
|
23
|
-
}
|
|
24
|
-
return results;
|
|
25
|
-
}
|
|
26
20
|
getAdapter() {
|
|
27
21
|
return {
|
|
28
22
|
create: async (options, successCallback, errorCallback) => {
|
|
29
23
|
try {
|
|
30
|
-
|
|
24
|
+
this.getDatabase(options.name, options.location);
|
|
31
25
|
successCallback({ success: true, database: options.name });
|
|
32
26
|
}
|
|
33
27
|
catch (error) {
|
|
@@ -35,61 +29,43 @@ export class DatabaseNative {
|
|
|
35
29
|
}
|
|
36
30
|
},
|
|
37
31
|
execute: async (options, successCallback, errorCallback) => {
|
|
32
|
+
var _a;
|
|
38
33
|
try {
|
|
39
|
-
const db =
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return false;
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
successCallback(results);
|
|
34
|
+
const db = this.getDatabase(options.dbName);
|
|
35
|
+
const res = db.execute(options.query, options.params);
|
|
36
|
+
// op-sqlite v15+ returns rows as an array natively in most configurations
|
|
37
|
+
// Check for _array for backward compatibility with some bridges or older habits
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
const rows = ((_a = res.rows) === null || _a === void 0 ? void 0 : _a._array) || res.rows || [];
|
|
40
|
+
successCallback(rows);
|
|
51
41
|
}
|
|
52
42
|
catch (error) {
|
|
53
43
|
errorCallback(error instanceof Error ? error.message : String(error));
|
|
54
44
|
}
|
|
55
45
|
},
|
|
56
46
|
executeStatementOnPath: async (dbPath, sqlQuery, callback) => {
|
|
57
|
-
var _a;
|
|
47
|
+
var _a, _b;
|
|
58
48
|
try {
|
|
59
49
|
const dbName = ((_a = dbPath.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(".db", "")) || "default";
|
|
60
|
-
const db =
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}, () => {
|
|
66
|
-
resolve([]);
|
|
67
|
-
return false;
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
callback(results);
|
|
50
|
+
const db = this.getDatabase(dbName);
|
|
51
|
+
const res = db.execute(sqlQuery);
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
const rows = ((_b = res.rows) === null || _b === void 0 ? void 0 : _b._array) || res.rows || [];
|
|
54
|
+
callback(rows);
|
|
72
55
|
}
|
|
73
56
|
catch (error) {
|
|
74
57
|
callback([]);
|
|
75
58
|
}
|
|
76
59
|
},
|
|
77
60
|
selectFromPath: async (dbPath, sqlQuery, callback) => {
|
|
78
|
-
var _a;
|
|
61
|
+
var _a, _b;
|
|
79
62
|
try {
|
|
80
63
|
const dbName = ((_a = dbPath.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(".db", "")) || "default";
|
|
81
|
-
const db =
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}, () => {
|
|
87
|
-
resolve([]);
|
|
88
|
-
return false;
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
callback(results);
|
|
64
|
+
const db = this.getDatabase(dbName);
|
|
65
|
+
const res = db.execute(sqlQuery);
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
const rows = ((_b = res.rows) === null || _b === void 0 ? void 0 : _b._array) || res.rows || [];
|
|
68
|
+
callback(rows);
|
|
93
69
|
}
|
|
94
70
|
catch (error) {
|
|
95
71
|
callback([]);
|
|
@@ -99,7 +75,7 @@ export class DatabaseNative {
|
|
|
99
75
|
var _a;
|
|
100
76
|
try {
|
|
101
77
|
const dbName = ((_a = dbPath.split("/").pop()) === null || _a === void 0 ? void 0 : _a.replace(".db", "")) || "default";
|
|
102
|
-
|
|
78
|
+
this.getDatabase(dbName);
|
|
103
79
|
callback();
|
|
104
80
|
}
|
|
105
81
|
catch (error) {
|
|
@@ -125,19 +101,12 @@ export class DatabaseNative {
|
|
|
125
101
|
},
|
|
126
102
|
deleteUserData: async (options, callback, errorCallback) => {
|
|
127
103
|
try {
|
|
128
|
-
const db =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const params = options.userId ? [options.userId] : [];
|
|
135
|
-
tx.executeSql(query, params, () => resolve(), (_, error) => {
|
|
136
|
-
reject(error);
|
|
137
|
-
return false;
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
});
|
|
104
|
+
const db = this.getDatabase(options.dbName);
|
|
105
|
+
const query = options.userId
|
|
106
|
+
? "DELETE FROM user_data WHERE user_id = ?"
|
|
107
|
+
: "DELETE FROM user_data";
|
|
108
|
+
const params = options.userId ? [options.userId] : [];
|
|
109
|
+
db.execute(query, params);
|
|
141
110
|
callback();
|
|
142
111
|
}
|
|
143
112
|
catch (error) {
|
|
@@ -2,7 +2,7 @@ import { IFileEntry, FileSystemEncoding } from './FileSystem.types';
|
|
|
2
2
|
/**
|
|
3
3
|
* FileSystem class
|
|
4
4
|
* Handles file system operations for React Native
|
|
5
|
-
* - iOS/Android/Windows: Uses
|
|
5
|
+
* - iOS/Android/Windows: Uses react-native-fs
|
|
6
6
|
* - Web: Limited functionality (not supported)
|
|
7
7
|
*/
|
|
8
8
|
export declare class FileSystem {
|
|
@@ -6,7 +6,7 @@ const fsImpl = Platform.OS === 'web' ? new FileSystemWeb() : new FileSystemNativ
|
|
|
6
6
|
/**
|
|
7
7
|
* FileSystem class
|
|
8
8
|
* Handles file system operations for React Native
|
|
9
|
-
* - iOS/Android/Windows: Uses
|
|
9
|
+
* - iOS/Android/Windows: Uses react-native-fs
|
|
10
10
|
* - Web: Limited functionality (not supported)
|
|
11
11
|
*/
|
|
12
12
|
export class FileSystem {
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { IFileEntry, IDirectoryReader } from './FileSystem.types';
|
|
2
|
+
/**
|
|
3
|
+
* DirectoryReader implementation for React Native
|
|
4
|
+
* Wraps RNFS.readDir to match the Cordova DirectoryReader interface
|
|
5
|
+
*/
|
|
6
|
+
export declare class RNDirectoryReader implements IDirectoryReader {
|
|
7
|
+
private dirPath;
|
|
8
|
+
constructor(dirPath: string);
|
|
9
|
+
readEntries(callback: (entries: IFileEntry[]) => void, errorCallback: (error: any) => void): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* FileEntry implementation for React Native
|
|
13
|
+
* Wraps react-native-fs to match the Cordova FileEntry interface
|
|
14
|
+
*/
|
|
2
15
|
export declare class RNFileEntry implements IFileEntry {
|
|
3
16
|
isFile: boolean;
|
|
4
17
|
isDirectory: boolean;
|
|
@@ -6,21 +19,37 @@ export declare class RNFileEntry implements IFileEntry {
|
|
|
6
19
|
fullPath: string;
|
|
7
20
|
nativeURL: string;
|
|
8
21
|
filesystem: any;
|
|
9
|
-
constructor(
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
constructor(path: string, isDirectory?: boolean);
|
|
23
|
+
/**
|
|
24
|
+
* Remove a file or empty directory
|
|
25
|
+
*/
|
|
26
|
+
remove(callback: () => void, errorCallback: (error: any) => void): void;
|
|
27
|
+
/**
|
|
28
|
+
* Remove a directory and all its contents recursively
|
|
29
|
+
*/
|
|
30
|
+
removeRecursively(callback: () => void, errorCallback: (error: any) => void): void;
|
|
31
|
+
/**
|
|
32
|
+
* Create a directory reader for reading directory contents
|
|
33
|
+
*/
|
|
12
34
|
createReader(): IDirectoryReader;
|
|
35
|
+
/**
|
|
36
|
+
* Get or create a subdirectory
|
|
37
|
+
*/
|
|
13
38
|
getDirectory(path: string, options: {
|
|
14
39
|
create: boolean;
|
|
15
|
-
},
|
|
40
|
+
}, callback: (entry: IFileEntry) => void, errorCallback: (error: any) => void): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get or create a file
|
|
43
|
+
*/
|
|
16
44
|
getFile(path: string, options: {
|
|
17
45
|
create: boolean;
|
|
18
|
-
},
|
|
46
|
+
}, callback: (entry: IFileEntry) => void, errorCallback: (error: any) => void): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get file metadata
|
|
49
|
+
*/
|
|
19
50
|
file(callback: (file: any) => void, errorCallback: (error: any) => void): void;
|
|
51
|
+
/**
|
|
52
|
+
* Create a file writer for writing to the file
|
|
53
|
+
*/
|
|
20
54
|
createWriter(callback: (writer: any) => void, errorCallback: (error: any) => void): void;
|
|
21
55
|
}
|
|
22
|
-
export declare class RNDirectoryReader implements IDirectoryReader {
|
|
23
|
-
private localPath;
|
|
24
|
-
constructor(localPath: string);
|
|
25
|
-
readEntries(successCallback: (entries: IFileEntry[]) => void, errorCallback: (error: any) => void): void;
|
|
26
|
-
}
|
|
@@ -1,114 +1,179 @@
|
|
|
1
|
-
import * as RNFS from '
|
|
1
|
+
import * as RNFS from 'react-native-fs';
|
|
2
|
+
/**
|
|
3
|
+
* DirectoryReader implementation for React Native
|
|
4
|
+
* Wraps RNFS.readDir to match the Cordova DirectoryReader interface
|
|
5
|
+
*/
|
|
6
|
+
export class RNDirectoryReader {
|
|
7
|
+
constructor(dirPath) {
|
|
8
|
+
this.dirPath = dirPath;
|
|
9
|
+
}
|
|
10
|
+
readEntries(callback, errorCallback) {
|
|
11
|
+
RNFS.readDir(this.dirPath)
|
|
12
|
+
.then(items => {
|
|
13
|
+
const entries = items.map(item => new RNFileEntry(item.path, item.isDirectory()));
|
|
14
|
+
callback(entries);
|
|
15
|
+
})
|
|
16
|
+
.catch(errorCallback);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* FileEntry implementation for React Native
|
|
21
|
+
* Wraps react-native-fs to match the Cordova FileEntry interface
|
|
22
|
+
*/
|
|
2
23
|
export class RNFileEntry {
|
|
3
|
-
constructor(
|
|
4
|
-
this.
|
|
24
|
+
constructor(path, isDirectory = false) {
|
|
25
|
+
this.fullPath = path;
|
|
26
|
+
this.nativeURL = `file://${path}`;
|
|
5
27
|
this.isDirectory = isDirectory;
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
8
|
-
this.
|
|
9
|
-
this.filesystem = filesystem;
|
|
28
|
+
this.isFile = !isDirectory;
|
|
29
|
+
this.name = path.split('/').pop() || '';
|
|
30
|
+
this.filesystem = null;
|
|
10
31
|
}
|
|
11
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Remove a file or empty directory
|
|
34
|
+
*/
|
|
35
|
+
remove(callback, errorCallback) {
|
|
12
36
|
RNFS.unlink(this.fullPath)
|
|
13
|
-
.then(() =>
|
|
14
|
-
.catch(
|
|
37
|
+
.then(() => callback())
|
|
38
|
+
.catch(errorCallback);
|
|
15
39
|
}
|
|
16
|
-
|
|
17
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Remove a directory and all its contents recursively
|
|
42
|
+
*/
|
|
43
|
+
removeRecursively(callback, errorCallback) {
|
|
44
|
+
// RNFS.unlink works recursively for directories
|
|
45
|
+
RNFS.unlink(this.fullPath)
|
|
46
|
+
.then(() => callback())
|
|
47
|
+
.catch(errorCallback);
|
|
18
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a directory reader for reading directory contents
|
|
51
|
+
*/
|
|
19
52
|
createReader() {
|
|
20
53
|
return new RNDirectoryReader(this.fullPath);
|
|
21
54
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Get or create a subdirectory
|
|
57
|
+
*/
|
|
58
|
+
getDirectory(path, options, callback, errorCallback) {
|
|
59
|
+
const dirPath = `${this.fullPath}/${path}`;
|
|
60
|
+
RNFS.exists(dirPath)
|
|
61
|
+
.then(exists => {
|
|
62
|
+
if (exists) {
|
|
63
|
+
return RNFS.stat(dirPath).then(stats => {
|
|
64
|
+
if (stats.isDirectory()) {
|
|
65
|
+
callback(new RNFileEntry(dirPath, true));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
errorCallback(new Error('Path exists but is not a directory'));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else if (options.create) {
|
|
73
|
+
return RNFS.mkdir(dirPath).then(() => {
|
|
74
|
+
callback(new RNFileEntry(dirPath, true));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
errorCallback(new Error('Directory does not exist'));
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
.catch(errorCallback);
|
|
40
82
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Get or create a file
|
|
85
|
+
*/
|
|
86
|
+
getFile(path, options, callback, errorCallback) {
|
|
87
|
+
const filePath = `${this.fullPath}/${path}`;
|
|
88
|
+
RNFS.exists(filePath)
|
|
89
|
+
.then(exists => {
|
|
90
|
+
if (exists) {
|
|
91
|
+
return RNFS.stat(filePath).then(stats => {
|
|
92
|
+
if (!stats.isDirectory()) {
|
|
93
|
+
callback(new RNFileEntry(filePath, false));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
errorCallback(new Error('Path exists but is a directory'));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else if (options.create) {
|
|
101
|
+
// Create empty file
|
|
102
|
+
return RNFS.writeFile(filePath, '', 'utf8').then(() => {
|
|
103
|
+
callback(new RNFileEntry(filePath, false));
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
errorCallback(new Error('File does not exist'));
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
.catch(errorCallback);
|
|
66
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Get file metadata
|
|
114
|
+
*/
|
|
67
115
|
file(callback, errorCallback) {
|
|
68
|
-
RNFS.stat(this.fullPath)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
slice: () => { }
|
|
116
|
+
RNFS.stat(this.fullPath)
|
|
117
|
+
.then(stats => {
|
|
118
|
+
// Create a File-like object with metadata
|
|
119
|
+
const fileObject = {
|
|
120
|
+
name: this.fullPath.split('/').pop() || '',
|
|
121
|
+
size: stats.size,
|
|
122
|
+
type: '', // RNFS doesn't provide MIME type directly
|
|
123
|
+
lastModified: new Date(stats.mtime).getTime(),
|
|
124
|
+
lastModifiedDate: new Date(stats.mtime),
|
|
125
|
+
path: this.fullPath,
|
|
79
126
|
};
|
|
80
|
-
callback(
|
|
81
|
-
})
|
|
127
|
+
callback(fileObject);
|
|
128
|
+
})
|
|
129
|
+
.catch(errorCallback);
|
|
82
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Create a file writer for writing to the file
|
|
133
|
+
*/
|
|
83
134
|
createWriter(callback, errorCallback) {
|
|
135
|
+
// Create a FileWriter-like object
|
|
84
136
|
const writer = {
|
|
137
|
+
filePath: this.fullPath,
|
|
85
138
|
write: (data) => {
|
|
86
|
-
RNFS.writeFile(this.fullPath, data, 'utf8')
|
|
139
|
+
return RNFS.writeFile(this.fullPath, data, 'utf8')
|
|
87
140
|
.then(() => { if (writer.onwriteend)
|
|
88
141
|
writer.onwriteend(); })
|
|
89
142
|
.catch((err) => { if (writer.onerror)
|
|
90
143
|
writer.onerror(err); });
|
|
91
144
|
},
|
|
92
|
-
|
|
93
|
-
|
|
145
|
+
append: (data) => {
|
|
146
|
+
return RNFS.appendFile(this.fullPath, data, 'utf8')
|
|
147
|
+
.then(() => { if (writer.onwriteend)
|
|
148
|
+
writer.onwriteend(); })
|
|
149
|
+
.catch((err) => { if (writer.onerror)
|
|
150
|
+
writer.onerror(err); });
|
|
151
|
+
},
|
|
152
|
+
truncate: (size) => {
|
|
153
|
+
// Read file, truncate content, and write back
|
|
154
|
+
return RNFS.readFile(this.fullPath, 'utf8')
|
|
155
|
+
.then(content => {
|
|
156
|
+
const truncated = content.substring(0, size);
|
|
157
|
+
return RNFS.writeFile(this.fullPath, truncated, 'utf8')
|
|
158
|
+
.then(() => { if (writer.onwriteend)
|
|
159
|
+
writer.onwriteend(); });
|
|
160
|
+
})
|
|
161
|
+
.catch((err) => { if (writer.onerror)
|
|
162
|
+
writer.onerror(err); });
|
|
163
|
+
},
|
|
164
|
+
seek: (offset) => {
|
|
165
|
+
// Not directly supported in RNFS
|
|
166
|
+
// This would need to be implemented with read/write operations
|
|
167
|
+
return Promise.resolve();
|
|
168
|
+
},
|
|
94
169
|
onwriteend: null,
|
|
95
170
|
onerror: null
|
|
96
171
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
readEntries(successCallback, errorCallback) {
|
|
105
|
-
RNFS.readDir(this.localPath)
|
|
106
|
-
.then((result) => {
|
|
107
|
-
const entries = result.map(fileStat => {
|
|
108
|
-
return new RNFileEntry(fileStat.isFile(), fileStat.isDirectory(), fileStat.name || fileStat.path.split('/').pop() || '', fileStat.path, "file://" + fileStat.path);
|
|
109
|
-
});
|
|
110
|
-
successCallback(entries);
|
|
111
|
-
})
|
|
112
|
-
.catch((err) => errorCallback(err));
|
|
172
|
+
try {
|
|
173
|
+
callback(writer);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
errorCallback(error);
|
|
177
|
+
}
|
|
113
178
|
}
|
|
114
179
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as RNFS from '
|
|
1
|
+
import * as RNFS from 'react-native-fs';
|
|
2
2
|
import { IFileEntry, IFileSystem, FileSystemEncoding } from '../FileSystem.types';
|
|
3
3
|
export declare class FileSystemNative implements IFileSystem {
|
|
4
4
|
getDocumentDirectory(): string;
|
|
@@ -10,8 +10,8 @@ export declare class FileSystemNative implements IFileSystem {
|
|
|
10
10
|
writeFile(path: string, content: string, encoding?: FileSystemEncoding): Promise<void>;
|
|
11
11
|
deleteFile(path: string): Promise<void>;
|
|
12
12
|
exists(path: string): Promise<boolean>;
|
|
13
|
-
stat(path: string): Promise<RNFS.
|
|
14
|
-
readDir(path: string): Promise<RNFS.
|
|
13
|
+
stat(path: string): Promise<RNFS.StatResult>;
|
|
14
|
+
readDir(path: string): Promise<RNFS.ReadDirItem[]>;
|
|
15
15
|
copyFile(source: string, destination: string): Promise<void>;
|
|
16
16
|
moveFile(source: string, destination: string): Promise<void>;
|
|
17
17
|
}
|
|
@@ -1,35 +1,52 @@
|
|
|
1
|
-
import * as RNFS from '
|
|
1
|
+
import * as RNFS from 'react-native-fs';
|
|
2
2
|
import { RNFileEntry } from '../RNFileEntry';
|
|
3
3
|
export class FileSystemNative {
|
|
4
4
|
getDocumentDirectory() {
|
|
5
5
|
return RNFS.DocumentDirectoryPath;
|
|
6
6
|
}
|
|
7
7
|
async resolveLocalFileSystemURL(url) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
// Remove file:// prefix if present
|
|
10
|
+
const path = url.replace('file://', '');
|
|
11
|
+
RNFS.exists(path)
|
|
12
|
+
.then(exists => {
|
|
13
|
+
if (!exists) {
|
|
14
|
+
reject(new Error(`Path does not exist: ${path}`));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
return RNFS.stat(path).then(stats => {
|
|
18
|
+
resolve(new RNFileEntry(path, stats.isDirectory()));
|
|
19
|
+
});
|
|
20
|
+
})
|
|
21
|
+
.catch(reject);
|
|
22
|
+
});
|
|
19
23
|
}
|
|
20
24
|
async getFolderBasedOnUserId(userId) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
const userPath = `${RNFS.DocumentDirectoryPath}/${userId}`;
|
|
26
|
+
try {
|
|
27
|
+
const exists = await RNFS.exists(userPath);
|
|
28
|
+
if (!exists) {
|
|
29
|
+
// Create the directory if it doesn't exist
|
|
30
|
+
await RNFS.mkdir(userPath);
|
|
31
|
+
}
|
|
32
|
+
// Return the native URL format
|
|
33
|
+
return `file://${userPath}`;
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw new Error(`Failed to get/create folder for user ${userId}: ${error}`);
|
|
25
37
|
}
|
|
26
|
-
return userFolder;
|
|
27
38
|
}
|
|
28
39
|
async deleteUserFolder(userId) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
const userPath = `${RNFS.DocumentDirectoryPath}/${userId}`;
|
|
41
|
+
try {
|
|
42
|
+
const exists = await RNFS.exists(userPath);
|
|
43
|
+
if (exists) {
|
|
44
|
+
// RNFS.unlink removes directories recursively
|
|
45
|
+
await RNFS.unlink(userPath);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new Error(`Failed to delete folder for user ${userId}: ${error}`);
|
|
33
50
|
}
|
|
34
51
|
}
|
|
35
52
|
async createDirectory(path) {
|
|
@@ -4,14 +4,14 @@ import { Platform } from 'react-native';
|
|
|
4
4
|
let RNFS = null;
|
|
5
5
|
let zip = null;
|
|
6
6
|
try {
|
|
7
|
-
RNFS = require('
|
|
7
|
+
RNFS = require('react-native-fs');
|
|
8
8
|
if (!RNFS || !RNFS.DocumentDirectoryPath) {
|
|
9
|
-
console.error('❌
|
|
9
|
+
console.error('❌ react-native-fs is not properly linked. Please run: cd ios && pod install (iOS) or rebuild your Android app.');
|
|
10
10
|
RNFS = null;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
catch (e) {
|
|
14
|
-
console.error('❌
|
|
14
|
+
console.error('❌ react-native-fs module not found. Please install: npm install react-native-fs');
|
|
15
15
|
}
|
|
16
16
|
try {
|
|
17
17
|
const zipModule = require('react-native-zip-archive');
|
|
@@ -2,14 +2,14 @@ import * as pako from 'pako';
|
|
|
2
2
|
// Import with null safety
|
|
3
3
|
let RNFS = null;
|
|
4
4
|
try {
|
|
5
|
-
RNFS = require('
|
|
5
|
+
RNFS = require('react-native-fs');
|
|
6
6
|
if (!RNFS || !RNFS.DocumentDirectoryPath) {
|
|
7
|
-
console.error('❌
|
|
7
|
+
console.error('❌ react-native-fs is not properly linked.');
|
|
8
8
|
RNFS = null;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
catch (e) {
|
|
12
|
-
console.error('❌
|
|
12
|
+
console.error('❌ react-native-fs module not found.');
|
|
13
13
|
}
|
|
14
14
|
export class LoggerWindows {
|
|
15
15
|
constructor() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unvired/react-native-unvired-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"description": "Unvired SDK for React Native with logging, database, notifications, and file system support",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"types": "dist/main.d.ts",
|
|
@@ -24,13 +24,14 @@
|
|
|
24
24
|
"author": "Unvired",
|
|
25
25
|
"license": "UNLICENSED",
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"
|
|
27
|
+
"react-native-fs": "^2.20.0",
|
|
28
28
|
"@react-native-firebase/messaging": "^21.8.1",
|
|
29
|
-
"react": "
|
|
30
|
-
"react-native": "
|
|
31
|
-
"react-native-device-info": "^
|
|
32
|
-
"
|
|
33
|
-
"react-native-zip-archive": "^7.0.2"
|
|
29
|
+
"react": "19.2.0",
|
|
30
|
+
"react-native": "0.83.1",
|
|
31
|
+
"react-native-device-info": "^15.0.1",
|
|
32
|
+
"@op-engineering/op-sqlite": "^15.2.5",
|
|
33
|
+
"react-native-zip-archive": "^7.0.2",
|
|
34
|
+
"@react-native-async-storage/async-storage": "^2.2.0"
|
|
34
35
|
},
|
|
35
36
|
"peerDependenciesMeta": {
|
|
36
37
|
"@react-native-firebase/messaging": {
|
|
@@ -38,17 +39,24 @@
|
|
|
38
39
|
}
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@types/react": "^
|
|
42
|
-
"@types/react-native": "^0.
|
|
43
|
-
"@types/react-native-sqlite-storage": "^6.0.5",
|
|
42
|
+
"@types/react": "^19.0.0",
|
|
43
|
+
"@types/react-native": "^0.73.0",
|
|
44
44
|
"@types/sql.js": "^1.4.9",
|
|
45
45
|
"eslint": "^8.0.0",
|
|
46
46
|
"jest": "^29.0.0",
|
|
47
47
|
"prettier": "^3.0.0",
|
|
48
|
-
"typescript": "^5.0.0"
|
|
48
|
+
"typescript": "^5.0.0",
|
|
49
|
+
"react-native-fs": "^2.20.0",
|
|
50
|
+
"@op-engineering/op-sqlite": "^15.2.5",
|
|
51
|
+
"react-native-zip-archive": "^7.0.2",
|
|
52
|
+
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
53
|
+
"react-native-device-info": "^15.0.1",
|
|
54
|
+
"@react-native-firebase/messaging": "^21.8.1"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=22.0.0"
|
|
49
58
|
},
|
|
50
59
|
"dependencies": {
|
|
51
|
-
"@react-native-async-storage/async-storage": "^2.2.0",
|
|
52
60
|
"@types/pako": "^2.0.4",
|
|
53
61
|
"pako": "^2.1.0",
|
|
54
62
|
"sql.js": "^1.10.3"
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform Adapter Interface
|
|
3
|
+
*
|
|
4
|
+
* This interface abstracts platform-specific functionality so that the core SDK
|
|
5
|
+
* can work with both Cordova and React Native platforms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export enum DatabaseType {
|
|
9
|
+
FrameworkDb = 'FrameworkDb',
|
|
10
|
+
AppDb = 'AppDb'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IDeviceInfo {
|
|
14
|
+
platform: string;
|
|
15
|
+
model: string;
|
|
16
|
+
version: string;
|
|
17
|
+
isMobile: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface IFileEntry {
|
|
21
|
+
fullPath: string;
|
|
22
|
+
nativeURL: string;
|
|
23
|
+
isDirectory?: boolean;
|
|
24
|
+
remove(callback: () => void, errorCallback: (error: any) => void): void;
|
|
25
|
+
removeRecursively(callback: () => void, errorCallback: (error: any) => void): void;
|
|
26
|
+
createReader(): IDirectoryReader;
|
|
27
|
+
getDirectory?(path: string, options: { create: boolean }, callback: (entry: IFileEntry) => void, errorCallback: (error: any) => void): void;
|
|
28
|
+
getFile?(path: string, options: { create: boolean }, callback: (entry: IFileEntry) => void, errorCallback: (error: any) => void): void;
|
|
29
|
+
file?(callback: (file: File) => void, errorCallback: (error: any) => void): void;
|
|
30
|
+
createWriter?(callback: (writer: any) => void, errorCallback: (error: any) => void): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IDirectoryReader {
|
|
34
|
+
readEntries(callback: (entries: IFileEntry[]) => void, errorCallback: (error: any) => void): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface IDatabaseAdapter {
|
|
38
|
+
create(options: { userId: string }, successCallback: (result: any) => void, errorCallback: (error: any) => void): void;
|
|
39
|
+
execute(options: any, successCallback: (result: any) => void, errorCallback: (error: any) => void): void;
|
|
40
|
+
executeStatementOnPath(dbPath: string, sqlQuery: string, callback: (result: any) => void): void;
|
|
41
|
+
selectFromPath(dbPath: string, sqlQuery: string, callback: (result: any) => void): void;
|
|
42
|
+
createDatabase(dbPath: string, callback: () => void): void;
|
|
43
|
+
getDBFilePath(options: { dbType: string }, callback: (path: string) => void): void;
|
|
44
|
+
saveWebDB(options: { userId: string }, callback: (result: any) => void, errorCallback: (error: any) => void): void;
|
|
45
|
+
exportWebDB(options: { userId: string }, callback: (result: any) => void, errorCallback: (error: any) => void): void;
|
|
46
|
+
deleteUserData(options: { userId: string }, callback: () => void, errorCallback: (error: any) => void): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface IPushNotificationAdapter {
|
|
50
|
+
requestPermission(options?: { forceShow: boolean }): Promise<void>;
|
|
51
|
+
getToken(): Promise<string>;
|
|
52
|
+
onTokenRefresh(callback: (token: string) => void): void;
|
|
53
|
+
onMessage(callback: (payload: any) => void): void;
|
|
54
|
+
onBackgroundMessage(callback: (payload: any) => void): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ILoggerAdapter {
|
|
58
|
+
logDebug(sourceClass: string, method: string, message: string): void;
|
|
59
|
+
logError(sourceClass: string, method: string, message: string): void;
|
|
60
|
+
logInfo(sourceClass: string, method: string, message: string): void;
|
|
61
|
+
setLogLevel(logLevel: string): void;
|
|
62
|
+
getLogFileURL(): Promise<string>;
|
|
63
|
+
getLogFileContent(): Promise<string>;
|
|
64
|
+
getBackupLogFileContent(): Promise<string>;
|
|
65
|
+
clearLogFile(): void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface IStorageAdapter {
|
|
69
|
+
getItem(key: string): void;
|
|
70
|
+
setItem(key: string, value: string): void;
|
|
71
|
+
removeItem(key: string): void;
|
|
72
|
+
clear(): void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
export interface ILocalStorageAdapter {
|
|
78
|
+
getItem(key: string): void;
|
|
79
|
+
setItem(key: string, value: string): void;
|
|
80
|
+
removeItem(key: string): void;
|
|
81
|
+
clear(): void;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Main Platform Adapter Interface
|
|
85
|
+
*/
|
|
86
|
+
export interface PlatformInterface {
|
|
87
|
+
// Device Information
|
|
88
|
+
getDeviceInfo(): Promise<IDeviceInfo>;
|
|
89
|
+
getPlatform(): string;
|
|
90
|
+
getFrontendType(): string;
|
|
91
|
+
getDocumentDirectory(): string;
|
|
92
|
+
|
|
93
|
+
// File System Operations
|
|
94
|
+
resolveLocalFileSystemURL(url: string): Promise<IFileEntry>;
|
|
95
|
+
getFolderBasedOnUserId(userId: string): Promise<string>;
|
|
96
|
+
deleteUserFolder(userId: string): Promise<void>;
|
|
97
|
+
|
|
98
|
+
// Database Operations
|
|
99
|
+
getDatabaseAdapter(): IDatabaseAdapter;
|
|
100
|
+
|
|
101
|
+
// Push Notifications
|
|
102
|
+
getPushNotificationAdapter(): IPushNotificationAdapter | null;
|
|
103
|
+
|
|
104
|
+
// Logging
|
|
105
|
+
getLoggerAdapter(): ILoggerAdapter;
|
|
106
|
+
|
|
107
|
+
// Storage
|
|
108
|
+
getStorageAdapter(): IStorageAdapter;
|
|
109
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import RNFS from 'react-native-fs';
|
|
2
|
+
export interface IDirectoryReader {
|
|
3
|
+
readEntries(callback: (entries: IFileEntry[]) => void, errorCallback: (error: any) => void): void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface IFileEntry {
|
|
7
|
+
fullPath: string;
|
|
8
|
+
nativeURL: string;
|
|
9
|
+
isDirectory?: boolean;
|
|
10
|
+
remove(callback: () => void, errorCallback: (error: any) => void): void;
|
|
11
|
+
removeRecursively(callback: () => void, errorCallback: (error: any) => void): void;
|
|
12
|
+
createReader(): IDirectoryReader;
|
|
13
|
+
getDirectory?(path: string, options: { create: boolean }, callback: (entry: IFileEntry) => void, errorCallback: (error: any) => void): void;
|
|
14
|
+
getFile?(path: string, options: { create: boolean }, callback: (entry: IFileEntry) => void, errorCallback: (error: any) => void): void;
|
|
15
|
+
file?(callback: (file: File) => void, errorCallback: (error: any) => void): void;
|
|
16
|
+
createWriter?(callback: (writer: any) => void, errorCallback: (error: any) => void): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* DirectoryReader implementation for React Native
|
|
20
|
+
* Wraps RNFS.readDir to match the Cordova DirectoryReader interface
|
|
21
|
+
*/
|
|
22
|
+
export class ReactNativeDirectoryReader implements IDirectoryReader {
|
|
23
|
+
private dirPath: string;
|
|
24
|
+
|
|
25
|
+
constructor(dirPath: string) {
|
|
26
|
+
this.dirPath = dirPath;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
readEntries(
|
|
30
|
+
callback: (entries: IFileEntry[]) => void,
|
|
31
|
+
errorCallback: (error: any) => void
|
|
32
|
+
): void {
|
|
33
|
+
RNFS.readDir(this.dirPath)
|
|
34
|
+
.then(items => {
|
|
35
|
+
const entries = items.map(item =>
|
|
36
|
+
new ReactNativeFileEntry(item.path, item.isDirectory())
|
|
37
|
+
);
|
|
38
|
+
callback(entries);
|
|
39
|
+
})
|
|
40
|
+
.catch(errorCallback);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* FileEntry implementation for React Native
|
|
46
|
+
* Wraps react-native-fs to match the Cordova FileEntry interface
|
|
47
|
+
*/
|
|
48
|
+
export class ReactNativeFileEntry implements IFileEntry {
|
|
49
|
+
fullPath: string;
|
|
50
|
+
nativeURL: string;
|
|
51
|
+
isDirectory?: boolean;
|
|
52
|
+
|
|
53
|
+
constructor(path: string, isDirectory: boolean = false) {
|
|
54
|
+
this.fullPath = path;
|
|
55
|
+
this.nativeURL = `file://${path}`;
|
|
56
|
+
this.isDirectory = isDirectory;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Remove a file or empty directory
|
|
61
|
+
*/
|
|
62
|
+
remove(callback: () => void, errorCallback: (error: any) => void): void {
|
|
63
|
+
RNFS.unlink(this.fullPath)
|
|
64
|
+
.then(() => callback())
|
|
65
|
+
.catch(errorCallback);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Remove a directory and all its contents recursively
|
|
70
|
+
*/
|
|
71
|
+
removeRecursively(callback: () => void, errorCallback: (error: any) => void): void {
|
|
72
|
+
// RNFS.unlink works recursively for directories
|
|
73
|
+
RNFS.unlink(this.fullPath)
|
|
74
|
+
.then(() => callback())
|
|
75
|
+
.catch(errorCallback);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create a directory reader for reading directory contents
|
|
80
|
+
*/
|
|
81
|
+
createReader(): IDirectoryReader {
|
|
82
|
+
return new ReactNativeDirectoryReader(this.fullPath);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get or create a subdirectory
|
|
87
|
+
*/
|
|
88
|
+
getDirectory(
|
|
89
|
+
path: string,
|
|
90
|
+
options: { create: boolean },
|
|
91
|
+
callback: (entry: IFileEntry) => void,
|
|
92
|
+
errorCallback: (error: any) => void
|
|
93
|
+
): void {
|
|
94
|
+
const dirPath = `${this.fullPath}/${path}`;
|
|
95
|
+
|
|
96
|
+
RNFS.exists(dirPath)
|
|
97
|
+
.then(exists => {
|
|
98
|
+
if (exists) {
|
|
99
|
+
return RNFS.stat(dirPath).then(stats => {
|
|
100
|
+
if (stats.isDirectory()) {
|
|
101
|
+
callback(new ReactNativeFileEntry(dirPath, true));
|
|
102
|
+
} else {
|
|
103
|
+
errorCallback(new Error('Path exists but is not a directory'));
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
} else if (options.create) {
|
|
107
|
+
return RNFS.mkdir(dirPath).then(() => {
|
|
108
|
+
callback(new ReactNativeFileEntry(dirPath, true));
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
errorCallback(new Error('Directory does not exist'));
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
.catch(errorCallback);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get or create a file
|
|
119
|
+
*/
|
|
120
|
+
getFile(
|
|
121
|
+
path: string,
|
|
122
|
+
options: { create: boolean },
|
|
123
|
+
callback: (entry: IFileEntry) => void,
|
|
124
|
+
errorCallback: (error: any) => void
|
|
125
|
+
): void {
|
|
126
|
+
const filePath = `${this.fullPath}/${path}`;
|
|
127
|
+
|
|
128
|
+
RNFS.exists(filePath)
|
|
129
|
+
.then(exists => {
|
|
130
|
+
if (exists) {
|
|
131
|
+
return RNFS.stat(filePath).then(stats => {
|
|
132
|
+
if (!stats.isDirectory()) {
|
|
133
|
+
callback(new ReactNativeFileEntry(filePath, false));
|
|
134
|
+
} else {
|
|
135
|
+
errorCallback(new Error('Path exists but is a directory'));
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
} else if (options.create) {
|
|
139
|
+
// Create empty file
|
|
140
|
+
return RNFS.writeFile(filePath, '', 'utf8').then(() => {
|
|
141
|
+
callback(new ReactNativeFileEntry(filePath, false));
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
errorCallback(new Error('File does not exist'));
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
.catch(errorCallback);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get file metadata
|
|
152
|
+
*/
|
|
153
|
+
file(callback: (file: File) => void, errorCallback: (error: any) => void): void {
|
|
154
|
+
RNFS.stat(this.fullPath)
|
|
155
|
+
.then(stats => {
|
|
156
|
+
// Create a File-like object with metadata
|
|
157
|
+
const fileObject: any = {
|
|
158
|
+
name: this.fullPath.split('/').pop() || '',
|
|
159
|
+
size: stats.size,
|
|
160
|
+
type: '', // RNFS doesn't provide MIME type directly
|
|
161
|
+
lastModified: new Date(stats.mtime).getTime(),
|
|
162
|
+
lastModifiedDate: new Date(stats.mtime),
|
|
163
|
+
path: this.fullPath,
|
|
164
|
+
};
|
|
165
|
+
callback(fileObject as File);
|
|
166
|
+
})
|
|
167
|
+
.catch(errorCallback);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Create a file writer for writing to the file
|
|
172
|
+
*/
|
|
173
|
+
createWriter(callback: (writer: any) => void, errorCallback: (error: any) => void): void {
|
|
174
|
+
// Create a FileWriter-like object
|
|
175
|
+
const writer = {
|
|
176
|
+
filePath: this.fullPath,
|
|
177
|
+
|
|
178
|
+
write: (data: string) => {
|
|
179
|
+
return RNFS.writeFile(this.fullPath, data, 'utf8');
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
append: (data: string) => {
|
|
183
|
+
return RNFS.appendFile(this.fullPath, data, 'utf8');
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
truncate: (size: number) => {
|
|
187
|
+
// Read file, truncate content, and write back
|
|
188
|
+
return RNFS.readFile(this.fullPath, 'utf8')
|
|
189
|
+
.then(content => {
|
|
190
|
+
const truncated = content.substring(0, size);
|
|
191
|
+
return RNFS.writeFile(this.fullPath, truncated, 'utf8');
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
seek: (offset: number) => {
|
|
196
|
+
// Not directly supported in RNFS
|
|
197
|
+
// This would need to be implemented with read/write operations
|
|
198
|
+
return Promise.resolve();
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
callback(writer);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
errorCallback(error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { PlatformInterface, IDeviceInfo, IFileEntry, IDatabaseAdapter, IPushNotificationAdapter, ILoggerAdapter, DatabaseType } from './PlatformInterface';
|
|
2
|
-
import { logger, LogLevel } from '
|
|
2
|
+
import { logger, LogLevel } from '../src/logger';
|
|
3
|
+
import { RNFileEntry } from '../src/file-system/RNFileEntry';
|
|
4
|
+
import { FileSystem } from '../src/file-system/FileSystem';
|
|
3
5
|
export class ReactNativePlatformAdapter implements PlatformInterface {
|
|
4
6
|
|
|
5
7
|
getDeviceInfo(): IDeviceInfo {
|
|
@@ -24,22 +26,20 @@ export class ReactNativePlatformAdapter implements PlatformInterface {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
getDocumentDirectory(): string {
|
|
27
|
-
//
|
|
28
|
-
return
|
|
29
|
+
// Return the document directory path for React Native
|
|
30
|
+
return FileSystem.getDocumentDirectory();
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
resolveLocalFileSystemURL(url: string): Promise<IFileEntry> {
|
|
32
|
-
|
|
33
|
-
return Promise.resolve({} as IFileEntry);
|
|
34
|
+
return FileSystem.resolveLocalFileSystemURL(url);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
async getFolderBasedOnUserId(userId: string): Promise<string> {
|
|
37
|
-
|
|
38
|
-
return "";
|
|
38
|
+
return FileSystem.getFolderBasedOnUserId(userId);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
async deleteUserFolder(userId: string): Promise<void> {
|
|
42
|
-
|
|
42
|
+
return FileSystem.deleteUserFolder(userId);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
getDatabaseAdapter(): IDatabaseAdapter {
|
|
@@ -139,7 +139,8 @@ export class ReactNativePlatformAdapter implements PlatformInterface {
|
|
|
139
139
|
},
|
|
140
140
|
getBackupLogFileContent: async () => {
|
|
141
141
|
// TODO: Implement backup log file content retrieval
|
|
142
|
-
|
|
142
|
+
const content = await logger.getBackupLogFileContent();
|
|
143
|
+
return content || '';
|
|
143
144
|
},
|
|
144
145
|
clearLogFile: async () => {
|
|
145
146
|
// TODO: Implement log file clearing
|