@webority-technologies/mobile-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/lib/commonjs/api/publicClient.js +80 -0
- package/lib/commonjs/api/userClient.js +368 -0
- package/lib/commonjs/auth/authStore.js +155 -0
- package/lib/commonjs/auth/index.js +44 -0
- package/lib/commonjs/auth/jwt.js +88 -0
- package/lib/commonjs/config/index.js +65 -0
- package/lib/commonjs/formatters/currency.js +69 -0
- package/lib/commonjs/formatters/date.js +168 -0
- package/lib/commonjs/formatters/index.js +77 -0
- package/lib/commonjs/formatters/initials.js +32 -0
- package/lib/commonjs/formatters/number.js +88 -0
- package/lib/commonjs/formatters/phone.js +67 -0
- package/lib/commonjs/index.js +383 -0
- package/lib/commonjs/initSDK.js +45 -0
- package/lib/commonjs/initUserAuth.js +38 -0
- package/lib/commonjs/logger/index.js +137 -0
- package/lib/commonjs/network/index.js +25 -0
- package/lib/commonjs/network/networkStatus.js +98 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/permissions/index.js +200 -0
- package/lib/commonjs/storage/index.js +193 -0
- package/lib/commonjs/utils/imageCompression.js +75 -0
- package/lib/commonjs/utils/index.js +13 -0
- package/lib/commonjs/validators/index.js +193 -0
- package/lib/commonjs/versionCheck/index.js +395 -0
- package/lib/module/api/publicClient.js +76 -0
- package/lib/module/api/userClient.js +360 -0
- package/lib/module/auth/authStore.js +148 -0
- package/lib/module/auth/index.js +5 -0
- package/lib/module/auth/jwt.js +82 -0
- package/lib/module/config/index.js +57 -0
- package/lib/module/formatters/currency.js +64 -0
- package/lib/module/formatters/date.js +160 -0
- package/lib/module/formatters/index.js +8 -0
- package/lib/module/formatters/initials.js +27 -0
- package/lib/module/formatters/number.js +81 -0
- package/lib/module/formatters/phone.js +61 -0
- package/lib/module/index.js +30 -0
- package/lib/module/initSDK.js +40 -0
- package/lib/module/initUserAuth.js +34 -0
- package/lib/module/logger/index.js +133 -0
- package/lib/module/network/index.js +4 -0
- package/lib/module/network/networkStatus.js +91 -0
- package/lib/module/package.json +1 -0
- package/lib/module/permissions/index.js +197 -0
- package/lib/module/storage/index.js +189 -0
- package/lib/module/utils/imageCompression.js +69 -0
- package/lib/module/utils/index.js +4 -0
- package/lib/module/validators/index.js +171 -0
- package/lib/module/versionCheck/index.js +390 -0
- package/lib/typescript/commonjs/api/publicClient.d.ts +11 -0
- package/lib/typescript/commonjs/api/userClient.d.ts +49 -0
- package/lib/typescript/commonjs/auth/authStore.d.ts +43 -0
- package/lib/typescript/commonjs/auth/index.d.ts +5 -0
- package/lib/typescript/commonjs/auth/jwt.d.ts +31 -0
- package/lib/typescript/commonjs/config/index.d.ts +49 -0
- package/lib/typescript/commonjs/formatters/currency.d.ts +24 -0
- package/lib/typescript/commonjs/formatters/date.d.ts +22 -0
- package/lib/typescript/commonjs/formatters/index.d.ts +8 -0
- package/lib/typescript/commonjs/formatters/initials.d.ts +11 -0
- package/lib/typescript/commonjs/formatters/number.d.ts +15 -0
- package/lib/typescript/commonjs/formatters/phone.d.ts +13 -0
- package/lib/typescript/commonjs/index.d.ts +30 -0
- package/lib/typescript/commonjs/initSDK.d.ts +40 -0
- package/lib/typescript/commonjs/initUserAuth.d.ts +22 -0
- package/lib/typescript/commonjs/logger/index.d.ts +42 -0
- package/lib/typescript/commonjs/network/index.d.ts +3 -0
- package/lib/typescript/commonjs/network/networkStatus.d.ts +27 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/permissions/index.d.ts +20 -0
- package/lib/typescript/commonjs/storage/index.d.ts +42 -0
- package/lib/typescript/commonjs/utils/imageCompression.d.ts +31 -0
- package/lib/typescript/commonjs/utils/index.d.ts +3 -0
- package/lib/typescript/commonjs/validators/index.d.ts +46 -0
- package/lib/typescript/commonjs/versionCheck/index.d.ts +80 -0
- package/lib/typescript/module/api/publicClient.d.ts +11 -0
- package/lib/typescript/module/api/userClient.d.ts +49 -0
- package/lib/typescript/module/auth/authStore.d.ts +43 -0
- package/lib/typescript/module/auth/index.d.ts +5 -0
- package/lib/typescript/module/auth/jwt.d.ts +31 -0
- package/lib/typescript/module/config/index.d.ts +49 -0
- package/lib/typescript/module/formatters/currency.d.ts +24 -0
- package/lib/typescript/module/formatters/date.d.ts +22 -0
- package/lib/typescript/module/formatters/index.d.ts +8 -0
- package/lib/typescript/module/formatters/initials.d.ts +11 -0
- package/lib/typescript/module/formatters/number.d.ts +15 -0
- package/lib/typescript/module/formatters/phone.d.ts +13 -0
- package/lib/typescript/module/index.d.ts +30 -0
- package/lib/typescript/module/initSDK.d.ts +40 -0
- package/lib/typescript/module/initUserAuth.d.ts +22 -0
- package/lib/typescript/module/logger/index.d.ts +42 -0
- package/lib/typescript/module/network/index.d.ts +3 -0
- package/lib/typescript/module/network/networkStatus.d.ts +27 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/permissions/index.d.ts +20 -0
- package/lib/typescript/module/storage/index.d.ts +42 -0
- package/lib/typescript/module/utils/imageCompression.d.ts +31 -0
- package/lib/typescript/module/utils/index.d.ts +3 -0
- package/lib/typescript/module/validators/index.d.ts +46 -0
- package/lib/typescript/module/versionCheck/index.d.ts +80 -0
- package/package.json +112 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { Logger } from "../logger/index.js";
|
|
5
|
+
const DEFAULT_STATUS = {
|
|
6
|
+
isConnected: true,
|
|
7
|
+
isInternetReachable: null,
|
|
8
|
+
type: 'unknown'
|
|
9
|
+
};
|
|
10
|
+
let netInfo = null;
|
|
11
|
+
let resolved = false;
|
|
12
|
+
const resolveNetInfo = () => {
|
|
13
|
+
if (resolved) {
|
|
14
|
+
return netInfo;
|
|
15
|
+
}
|
|
16
|
+
resolved = true;
|
|
17
|
+
try {
|
|
18
|
+
const mod = require('@react-native-community/netinfo');
|
|
19
|
+
netInfo = mod.default ?? mod;
|
|
20
|
+
} catch {
|
|
21
|
+
Logger.warn('[network] @react-native-community/netinfo is not installed; ' + 'useNetworkStatus() will report a default "online" status.');
|
|
22
|
+
netInfo = null;
|
|
23
|
+
}
|
|
24
|
+
return netInfo;
|
|
25
|
+
};
|
|
26
|
+
const normalize = state => {
|
|
27
|
+
if (!state) {
|
|
28
|
+
return DEFAULT_STATUS;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
isConnected: state.isConnected !== false,
|
|
32
|
+
isInternetReachable: state.isInternetReachable,
|
|
33
|
+
type: state.type ?? 'unknown'
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Imperative one-shot fetch of current network status. Resolves to the default
|
|
39
|
+
* online status when NetInfo is unavailable so callers never crash.
|
|
40
|
+
*/
|
|
41
|
+
export const getNetworkStatus = async () => {
|
|
42
|
+
const ni = resolveNetInfo();
|
|
43
|
+
if (!ni) {
|
|
44
|
+
return DEFAULT_STATUS;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const state = await ni.fetch();
|
|
48
|
+
return normalize(state);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
Logger.error('[network] getNetworkStatus failed', error);
|
|
51
|
+
return DEFAULT_STATUS;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Subscribe to network status changes. Returns an unsubscribe function.
|
|
57
|
+
*/
|
|
58
|
+
export const addNetworkStatusListener = listener => {
|
|
59
|
+
const ni = resolveNetInfo();
|
|
60
|
+
if (!ni) {
|
|
61
|
+
return () => undefined;
|
|
62
|
+
}
|
|
63
|
+
return ni.addEventListener(state => listener(normalize(state)));
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* React hook that returns the current network status and re-renders on change.
|
|
68
|
+
* Reports an optimistic online default when NetInfo is not installed.
|
|
69
|
+
*/
|
|
70
|
+
export const useNetworkStatus = () => {
|
|
71
|
+
const [status, setStatus] = useState(DEFAULT_STATUS);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
let mounted = true;
|
|
74
|
+
void getNetworkStatus().then(s => {
|
|
75
|
+
if (mounted) {
|
|
76
|
+
setStatus(s);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
const unsubscribe = addNetworkStatusListener(s => {
|
|
80
|
+
if (mounted) {
|
|
81
|
+
setStatus(s);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return () => {
|
|
85
|
+
mounted = false;
|
|
86
|
+
unsubscribe();
|
|
87
|
+
};
|
|
88
|
+
}, []);
|
|
89
|
+
return status;
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=networkStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Linking, Platform } from 'react-native';
|
|
4
|
+
import { Logger } from "../logger/index.js";
|
|
5
|
+
|
|
6
|
+
// Android only — legacy WRITE_EXTERNAL_STORAGE
|
|
7
|
+
|
|
8
|
+
let permsModule = null;
|
|
9
|
+
let resolved = false;
|
|
10
|
+
const resolveModule = () => {
|
|
11
|
+
if (resolved) {
|
|
12
|
+
return permsModule;
|
|
13
|
+
}
|
|
14
|
+
resolved = true;
|
|
15
|
+
try {
|
|
16
|
+
const mod = require('react-native-permissions');
|
|
17
|
+
permsModule = mod.default ?? mod;
|
|
18
|
+
} catch {
|
|
19
|
+
Logger.warn('[permissions] react-native-permissions is not installed; ' + 'permission checks will report "unavailable".');
|
|
20
|
+
permsModule = null;
|
|
21
|
+
}
|
|
22
|
+
return permsModule;
|
|
23
|
+
};
|
|
24
|
+
const mapToNative = kind => {
|
|
25
|
+
const m = resolveModule();
|
|
26
|
+
if (!m) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const {
|
|
30
|
+
IOS,
|
|
31
|
+
ANDROID
|
|
32
|
+
} = m.PERMISSIONS;
|
|
33
|
+
if (Platform.OS === 'ios') {
|
|
34
|
+
switch (kind) {
|
|
35
|
+
case 'camera':
|
|
36
|
+
return IOS.CAMERA ?? null;
|
|
37
|
+
case 'microphone':
|
|
38
|
+
return IOS.MICROPHONE ?? null;
|
|
39
|
+
case 'photos':
|
|
40
|
+
return IOS.PHOTO_LIBRARY ?? null;
|
|
41
|
+
case 'photosAdd':
|
|
42
|
+
return IOS.PHOTO_LIBRARY_ADD_ONLY ?? null;
|
|
43
|
+
case 'mediaLibrary':
|
|
44
|
+
return IOS.MEDIA_LIBRARY ?? null;
|
|
45
|
+
case 'location':
|
|
46
|
+
return IOS.LOCATION_WHEN_IN_USE ?? null;
|
|
47
|
+
case 'locationAlways':
|
|
48
|
+
return IOS.LOCATION_ALWAYS ?? null;
|
|
49
|
+
case 'contacts':
|
|
50
|
+
return IOS.CONTACTS ?? null;
|
|
51
|
+
case 'calendar':
|
|
52
|
+
return IOS.CALENDARS ?? null;
|
|
53
|
+
case 'reminders':
|
|
54
|
+
return IOS.REMINDERS ?? null;
|
|
55
|
+
case 'notifications':
|
|
56
|
+
return null;
|
|
57
|
+
// handled separately on iOS
|
|
58
|
+
case 'bluetooth':
|
|
59
|
+
return IOS.BLUETOOTH ?? null;
|
|
60
|
+
case 'storage':
|
|
61
|
+
return null;
|
|
62
|
+
default:
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
switch (kind) {
|
|
67
|
+
case 'camera':
|
|
68
|
+
return ANDROID.CAMERA ?? null;
|
|
69
|
+
case 'microphone':
|
|
70
|
+
return ANDROID.RECORD_AUDIO ?? null;
|
|
71
|
+
case 'photos':
|
|
72
|
+
case 'mediaLibrary':
|
|
73
|
+
return ANDROID.READ_MEDIA_IMAGES ?? ANDROID.READ_EXTERNAL_STORAGE ?? null;
|
|
74
|
+
case 'photosAdd':
|
|
75
|
+
return ANDROID.READ_MEDIA_IMAGES ?? null;
|
|
76
|
+
case 'location':
|
|
77
|
+
case 'locationAlways':
|
|
78
|
+
return ANDROID.ACCESS_FINE_LOCATION ?? null;
|
|
79
|
+
case 'contacts':
|
|
80
|
+
return ANDROID.READ_CONTACTS ?? null;
|
|
81
|
+
case 'calendar':
|
|
82
|
+
return ANDROID.READ_CALENDAR ?? null;
|
|
83
|
+
case 'reminders':
|
|
84
|
+
return null;
|
|
85
|
+
case 'notifications':
|
|
86
|
+
return ANDROID.POST_NOTIFICATIONS ?? null;
|
|
87
|
+
case 'bluetooth':
|
|
88
|
+
return ANDROID.BLUETOOTH_CONNECT ?? null;
|
|
89
|
+
case 'storage':
|
|
90
|
+
return ANDROID.WRITE_EXTERNAL_STORAGE ?? null;
|
|
91
|
+
default:
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const mapResult = raw => {
|
|
96
|
+
const m = resolveModule();
|
|
97
|
+
if (!m || !raw) {
|
|
98
|
+
return 'unavailable';
|
|
99
|
+
}
|
|
100
|
+
const {
|
|
101
|
+
RESULTS
|
|
102
|
+
} = m;
|
|
103
|
+
if (raw === RESULTS.GRANTED) {
|
|
104
|
+
return 'granted';
|
|
105
|
+
}
|
|
106
|
+
if (raw === RESULTS.DENIED) {
|
|
107
|
+
return 'denied';
|
|
108
|
+
}
|
|
109
|
+
if (raw === RESULTS.LIMITED) {
|
|
110
|
+
return 'limited';
|
|
111
|
+
}
|
|
112
|
+
if (raw === RESULTS.BLOCKED) {
|
|
113
|
+
return 'blocked';
|
|
114
|
+
}
|
|
115
|
+
return 'unavailable';
|
|
116
|
+
};
|
|
117
|
+
const checkPermission = async kind => {
|
|
118
|
+
const m = resolveModule();
|
|
119
|
+
if (!m) {
|
|
120
|
+
return 'unavailable';
|
|
121
|
+
}
|
|
122
|
+
const native = mapToNative(kind);
|
|
123
|
+
if (!native) {
|
|
124
|
+
return 'unavailable';
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const raw = await m.check(native);
|
|
128
|
+
return mapResult(raw);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
Logger.error(`[permissions] check(${kind}) failed`, error);
|
|
131
|
+
return 'unavailable';
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const requestPermission = async kind => {
|
|
135
|
+
const m = resolveModule();
|
|
136
|
+
if (!m) {
|
|
137
|
+
return 'unavailable';
|
|
138
|
+
}
|
|
139
|
+
const native = mapToNative(kind);
|
|
140
|
+
if (!native) {
|
|
141
|
+
return 'unavailable';
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const raw = await m.request(native);
|
|
145
|
+
return mapResult(raw);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
Logger.error(`[permissions] request(${kind}) failed`, error);
|
|
148
|
+
return 'unavailable';
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const openAppSettings = async () => {
|
|
152
|
+
const m = resolveModule();
|
|
153
|
+
if (!m) {
|
|
154
|
+
try {
|
|
155
|
+
await Linking.openSettings();
|
|
156
|
+
return true;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
Logger.error('[permissions] openAppSettings fallback failed', error);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
await m.openSettings();
|
|
164
|
+
return true;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
Logger.error('[permissions] openSettings failed', error);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Cross-platform permissions wrapper. Maps an abstract `PermissionKind`
|
|
173
|
+
* (camera, photos, location, notifications, …) to the right
|
|
174
|
+
* iOS / Android permission string under the hood.
|
|
175
|
+
*
|
|
176
|
+
* Requires `react-native-permissions` as an optional peer dependency;
|
|
177
|
+
* when missing, every check/request resolves to `'unavailable'`.
|
|
178
|
+
*/
|
|
179
|
+
export const Permissions = {
|
|
180
|
+
check: checkPermission,
|
|
181
|
+
request: requestPermission,
|
|
182
|
+
/** Convenience: returns true when the permission is `granted` or `limited`. */
|
|
183
|
+
isAuthorized: async kind => {
|
|
184
|
+
const s = await checkPermission(kind);
|
|
185
|
+
return s === 'granted' || s === 'limited';
|
|
186
|
+
},
|
|
187
|
+
/** Convenience: check, and request only if not yet granted. */
|
|
188
|
+
ensure: async kind => {
|
|
189
|
+
const s = await checkPermission(kind);
|
|
190
|
+
if (s === 'granted' || s === 'limited' || s === 'blocked' || s === 'unavailable') {
|
|
191
|
+
return s;
|
|
192
|
+
}
|
|
193
|
+
return requestPermission(kind);
|
|
194
|
+
},
|
|
195
|
+
openSettings: openAppSettings
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Logger } from "../logger/index.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Minimal AsyncStorage shape we depend on.
|
|
7
|
+
* Lets us avoid a hard import on `@react-native-async-storage/async-storage`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
let backend = null;
|
|
11
|
+
let resolved = false;
|
|
12
|
+
const resolveBackend = () => {
|
|
13
|
+
if (resolved) {
|
|
14
|
+
return backend;
|
|
15
|
+
}
|
|
16
|
+
resolved = true;
|
|
17
|
+
try {
|
|
18
|
+
const mod = require('@react-native-async-storage/async-storage');
|
|
19
|
+
backend = mod.default ?? mod;
|
|
20
|
+
} catch {
|
|
21
|
+
backend = null;
|
|
22
|
+
}
|
|
23
|
+
return backend;
|
|
24
|
+
};
|
|
25
|
+
const requireBackend = op => {
|
|
26
|
+
const b = resolveBackend();
|
|
27
|
+
if (!b) {
|
|
28
|
+
const err = new Error(`[Storage] @react-native-async-storage/async-storage is not installed; cannot ${op}. ` + 'Install the peer dep or call setStorageImplementation() with a custom backend.');
|
|
29
|
+
Logger.error(err.message);
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
return b;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Inject a custom storage backend (e.g. MMKV adapter) at runtime.
|
|
37
|
+
* Pass `null` to reset to the default AsyncStorage backend.
|
|
38
|
+
*/
|
|
39
|
+
export const setStorageImplementation = impl => {
|
|
40
|
+
backend = impl;
|
|
41
|
+
resolved = true;
|
|
42
|
+
};
|
|
43
|
+
const safeJsonParse = raw => {
|
|
44
|
+
if (raw == null) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(raw);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
Logger.warn('[Storage] Failed to parse JSON value, returning null', error);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const getItem = async key => {
|
|
55
|
+
try {
|
|
56
|
+
return await requireBackend('getItem').getItem(key);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
Logger.error(`[Storage] getItem failed for key "${key}"`, error);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const setItem = async (key, value) => {
|
|
63
|
+
try {
|
|
64
|
+
await requireBackend('setItem').setItem(key, value);
|
|
65
|
+
return true;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
Logger.error(`[Storage] setItem failed for key "${key}"`, error);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const removeItem = async key => {
|
|
72
|
+
try {
|
|
73
|
+
await requireBackend('removeItem').removeItem(key);
|
|
74
|
+
return true;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
Logger.error(`[Storage] removeItem failed for key "${key}"`, error);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const getJson = async key => {
|
|
81
|
+
const raw = await getItem(key);
|
|
82
|
+
return safeJsonParse(raw);
|
|
83
|
+
};
|
|
84
|
+
const setJson = async (key, value) => {
|
|
85
|
+
try {
|
|
86
|
+
return await setItem(key, JSON.stringify(value));
|
|
87
|
+
} catch (error) {
|
|
88
|
+
Logger.error(`[Storage] setJson failed for key "${key}"`, error);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const multiGet = async keys => {
|
|
93
|
+
try {
|
|
94
|
+
const b = requireBackend('multiGet');
|
|
95
|
+
if (!b.multiGet) {
|
|
96
|
+
const out = {};
|
|
97
|
+
await Promise.all(keys.map(async k => {
|
|
98
|
+
out[k] = await b.getItem(k);
|
|
99
|
+
}));
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
const pairs = await b.multiGet(keys);
|
|
103
|
+
const out = {};
|
|
104
|
+
for (const [k, v] of pairs) {
|
|
105
|
+
out[k] = v;
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
Logger.error('[Storage] multiGet failed', error);
|
|
110
|
+
return {};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const multiSet = async entries => {
|
|
114
|
+
try {
|
|
115
|
+
const b = requireBackend('multiSet');
|
|
116
|
+
const pairs = Object.entries(entries);
|
|
117
|
+
if (!b.multiSet) {
|
|
118
|
+
await Promise.all(pairs.map(([k, v]) => b.setItem(k, v)));
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
await b.multiSet(pairs);
|
|
122
|
+
return true;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
Logger.error('[Storage] multiSet failed', error);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const multiRemove = async keys => {
|
|
129
|
+
try {
|
|
130
|
+
const b = requireBackend('multiRemove');
|
|
131
|
+
if (!b.multiRemove) {
|
|
132
|
+
await Promise.all(keys.map(k => b.removeItem(k)));
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
await b.multiRemove(keys);
|
|
136
|
+
return true;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
Logger.error('[Storage] multiRemove failed', error);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const clear = async () => {
|
|
143
|
+
try {
|
|
144
|
+
const b = requireBackend('clear');
|
|
145
|
+
if (!b.clear) {
|
|
146
|
+
Logger.warn('[Storage] backend does not support clear()');
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
await b.clear();
|
|
150
|
+
return true;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
Logger.error('[Storage] clear failed', error);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const getAllKeys = async () => {
|
|
157
|
+
try {
|
|
158
|
+
const b = requireBackend('getAllKeys');
|
|
159
|
+
if (!b.getAllKeys) {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
const keys = await b.getAllKeys();
|
|
163
|
+
return [...keys];
|
|
164
|
+
} catch (error) {
|
|
165
|
+
Logger.error('[Storage] getAllKeys failed', error);
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Typed AsyncStorage wrapper with safe JSON helpers.
|
|
171
|
+
* All operations swallow errors (logging them) and return a falsy result
|
|
172
|
+
* so that storage problems never crash the app.
|
|
173
|
+
*
|
|
174
|
+
* Backed by `@react-native-async-storage/async-storage` by default;
|
|
175
|
+
* override with `setStorageImplementation()` for MMKV or custom adapters.
|
|
176
|
+
*/
|
|
177
|
+
export const Storage = {
|
|
178
|
+
getItem,
|
|
179
|
+
setItem,
|
|
180
|
+
removeItem,
|
|
181
|
+
getJson,
|
|
182
|
+
setJson,
|
|
183
|
+
multiGet,
|
|
184
|
+
multiSet,
|
|
185
|
+
multiRemove,
|
|
186
|
+
clear,
|
|
187
|
+
getAllKeys
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Logger } from "../logger/index.js";
|
|
4
|
+
let compressor = null;
|
|
5
|
+
let resolved = false;
|
|
6
|
+
const resolveCompressor = () => {
|
|
7
|
+
if (resolved) {
|
|
8
|
+
return compressor;
|
|
9
|
+
}
|
|
10
|
+
resolved = true;
|
|
11
|
+
try {
|
|
12
|
+
compressor = require('react-native-compressor');
|
|
13
|
+
} catch {
|
|
14
|
+
Logger.warn('[imageCompression] react-native-compressor is not installed; ' + 'compressImage() will return the original URI unchanged.');
|
|
15
|
+
compressor = null;
|
|
16
|
+
}
|
|
17
|
+
return compressor;
|
|
18
|
+
};
|
|
19
|
+
const EIGHT_MB = 8 * 1024 * 1024;
|
|
20
|
+
const THREE_MB = 3 * 1024 * 1024;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Bigger source files get compressed harder — a fixed quality either over-compresses
|
|
24
|
+
* small photos or leaves large ones too heavy to upload quickly. Unknown size falls
|
|
25
|
+
* back to the middle rung rather than assuming either extreme.
|
|
26
|
+
*/
|
|
27
|
+
export const qualityForFileSize = fileSize => {
|
|
28
|
+
if (fileSize == null) {
|
|
29
|
+
return 0.7;
|
|
30
|
+
}
|
|
31
|
+
if (fileSize > EIGHT_MB) {
|
|
32
|
+
return 0.6;
|
|
33
|
+
}
|
|
34
|
+
if (fileSize > THREE_MB) {
|
|
35
|
+
return 0.7;
|
|
36
|
+
}
|
|
37
|
+
return 0.8;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Compress an image referenced by a URI. Returns a new URI on success, or the
|
|
42
|
+
* original URI on failure / when the optional `react-native-compressor`
|
|
43
|
+
* dependency is missing — so callers can rely on always getting a usable URI.
|
|
44
|
+
*/
|
|
45
|
+
export const compressImage = async (uri, options = {}) => {
|
|
46
|
+
const lib = resolveCompressor();
|
|
47
|
+
if (!lib || !uri) {
|
|
48
|
+
return uri;
|
|
49
|
+
}
|
|
50
|
+
const {
|
|
51
|
+
maxSize = 1500,
|
|
52
|
+
output = 'jpg',
|
|
53
|
+
fileSize
|
|
54
|
+
} = options;
|
|
55
|
+
const quality = options.quality ?? qualityForFileSize(fileSize);
|
|
56
|
+
try {
|
|
57
|
+
return await lib.Image.compress(uri, {
|
|
58
|
+
maxWidth: maxSize,
|
|
59
|
+
maxHeight: maxSize,
|
|
60
|
+
quality,
|
|
61
|
+
compressionMethod: 'manual',
|
|
62
|
+
output
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
Logger.error('[imageCompression] compressImage failed; returning original URI', error);
|
|
66
|
+
return uri;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=imageCompression.js.map
|