react-native-rook-sdk 4.0.0-beta.2 → 5.0.0-beta.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/RNRookSdk.podspec +1 -1
- package/android/build.gradle +6 -0
- package/android/src/main/java/com/rooksdk/RookSdkModule.kt +1850 -6
- package/android/src/main/java/com/rooksdk/broadcasts/AndroidPermissionsReceiverTransmitter.kt +62 -0
- package/android/src/main/java/com/rooksdk/broadcasts/HealthConnectPermissionsReceiverTransmitter.kt +64 -0
- package/android/src/main/java/com/rooksdk/broadcasts/SamsungHealthPermissionsReceiverTransmitter.kt +44 -0
- package/android/src/main/java/com/rooksdk/utils/DatasourceUtils.kt +35 -0
- package/android/src/main/java/com/rooksdk/utils/PermissionConvertions.kt +21 -0
- package/android/src/main/java/com/rooksdk/utils/ReadableToWritable.kt +144 -0
- package/android/src/main/java/com/rooksdk/utils/RookDateTime.kt +26 -0
- package/android/src/main/java/com/rooksdk/utils/RookGsonBuilder.kt +20 -0
- package/android/src/main/java/com/rooksdk/utils/SamsungAvailability.kt +22 -0
- package/android/src/main/java/com/rooksdk/utils/Source.kt +6 -0
- package/android/src/main/java/com/rooksdk/utils/StringToSyncType.kt +41 -0
- package/android/src/main/java/com/rooksdk/utils/serializers/InstantSerializer.kt +18 -0
- package/android/src/main/java/com/rooksdk/utils/serializers/LocalDateSerializer.kt +21 -0
- package/android/src/main/java/com/rooksdk/utils/serializers/ZoneDateTimeSerializer.kt +18 -0
- package/ios/EncodableSDKState.swift +95 -0
- package/ios/NutritionMapper.swift +114 -0
- package/ios/RookConfiguration.swift +13 -0
- package/ios/RookData.swift +29 -0
- package/ios/RookEntry.swift +5 -4
- package/ios/RookPermissions.swift +33 -9
- package/ios/RookSdk.h +1 -13
- package/ios/RookSdk.mm +44 -237
- package/lib/module/NativeRookSdk.js +98 -10
- package/lib/module/NativeRookSdk.js.map +1 -1
- package/lib/module/context/RookSyncGateProvider.js +29 -21
- package/lib/module/context/RookSyncGateProvider.js.map +1 -1
- package/lib/module/context/RookSyncGateReducer.js +12 -2
- package/lib/module/context/RookSyncGateReducer.js.map +1 -1
- package/lib/module/hooks/useRookConfiguration.js +20 -1
- package/lib/module/hooks/useRookConfiguration.js.map +1 -1
- package/lib/module/hooks/useRookData.js +13 -4
- package/lib/module/hooks/useRookData.js.map +1 -1
- package/lib/module/hooks/useRookPermissions.js +13 -1
- package/lib/module/hooks/useRookPermissions.js.map +1 -1
- package/lib/module/hooks/useRookSync.js +1 -1
- package/lib/module/hooks/useRookSync.js.map +1 -1
- package/lib/module/hooks/useRookVariables.js +16 -1
- package/lib/module/hooks/useRookVariables.js.map +1 -1
- package/lib/module/modules/components/RookStateManager.js +31 -0
- package/lib/module/modules/components/RookStateManager.js.map +1 -0
- package/lib/module/modules/hook/useRookAutoSync.js +5 -4
- package/lib/module/modules/hook/useRookAutoSync.js.map +1 -1
- package/lib/module/modules/hook/useRookEmitter.js +61 -0
- package/lib/module/modules/hook/useRookEmitter.js.map +1 -0
- package/lib/module/types/AppleHealthNutritionEvent.js +72 -0
- package/lib/module/types/AppleHealthNutritionEvent.js.map +1 -0
- package/lib/module/types/HCMealData.js +61 -0
- package/lib/module/types/HCMealData.js.map +1 -0
- package/lib/module/types/WriteNutrition.js +14 -0
- package/lib/module/types/WriteNutrition.js.map +1 -0
- package/lib/module/utils/isValidDate.js +20 -0
- package/lib/module/utils/isValidDate.js.map +1 -1
- package/lib/typescript/src/NativeRookSdk.d.ts +622 -33
- package/lib/typescript/src/NativeRookSdk.d.ts.map +1 -1
- package/lib/typescript/src/context/RookSyncGateProvider.d.ts.map +1 -1
- package/lib/typescript/src/context/RookSyncGateReducer.d.ts.map +1 -1
- package/lib/typescript/src/context/RookSyncGateTypes.d.ts +14 -4
- package/lib/typescript/src/context/RookSyncGateTypes.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useRookConfiguration.d.ts +3 -1
- package/lib/typescript/src/hooks/useRookConfiguration.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useRookData.d.ts +2 -2
- package/lib/typescript/src/hooks/useRookData.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useRookPermissions.d.ts +2 -0
- package/lib/typescript/src/hooks/useRookPermissions.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useRookVariables.d.ts +2 -0
- package/lib/typescript/src/hooks/useRookVariables.d.ts.map +1 -1
- package/lib/typescript/src/modules/components/RookStateManager.d.ts +10 -0
- package/lib/typescript/src/modules/components/RookStateManager.d.ts.map +1 -0
- package/lib/typescript/src/modules/hook/useRookAutoSync.d.ts.map +1 -1
- package/lib/typescript/src/modules/hook/useRookEmitter.d.ts +4 -0
- package/lib/typescript/src/modules/hook/useRookEmitter.d.ts.map +1 -0
- package/lib/typescript/src/types/AppleHealthNutritionEvent.d.ts +76 -0
- package/lib/typescript/src/types/AppleHealthNutritionEvent.d.ts.map +1 -0
- package/lib/typescript/src/types/HCMealData.d.ts +60 -0
- package/lib/typescript/src/types/HCMealData.d.ts.map +1 -0
- package/lib/typescript/src/types/WriteNutrition.d.ts +61 -0
- package/lib/typescript/src/types/WriteNutrition.d.ts.map +1 -0
- package/lib/typescript/src/utils/isValidDate.d.ts +8 -0
- package/lib/typescript/src/utils/isValidDate.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeRookSdk.ts +682 -117
- package/src/context/RookSyncGateProvider.tsx +20 -16
- package/src/context/RookSyncGateReducer.ts +6 -2
- package/src/context/RookSyncGateTypes.ts +9 -3
- package/src/hooks/useRookConfiguration.ts +26 -2
- package/src/hooks/useRookData.ts +18 -7
- package/src/hooks/useRookPermissions.ts +16 -0
- package/src/hooks/useRookSync.ts +1 -1
- package/src/hooks/useRookVariables.ts +22 -1
- package/src/modules/components/RookStateManager.tsx +41 -0
- package/src/modules/hook/useRookAutoSync.ts +6 -4
- package/src/modules/hook/useRookEmitter.ts +94 -0
- package/src/types/AppleHealthNutritionEvent.ts +142 -0
- package/src/types/HCMealData.ts +114 -0
- package/src/types/WriteNutrition.ts +66 -0
- package/src/utils/isValidDate.ts +24 -0
|
@@ -7,12 +7,14 @@ import type {
|
|
|
7
7
|
} from './RookSyncGateTypes';
|
|
8
8
|
import { RookSyncGateContext } from './RookSyncGateContext';
|
|
9
9
|
import RookSdk from '../NativeRookSdk';
|
|
10
|
-
import {
|
|
10
|
+
import { RookStateManager } from '../modules/components/RookStateManager';
|
|
11
11
|
|
|
12
12
|
const initialState: RookSyncGateState = {
|
|
13
13
|
clientUUID: '',
|
|
14
14
|
environment: 'sandbox',
|
|
15
|
-
|
|
15
|
+
secret: '',
|
|
16
|
+
bundleId: undefined,
|
|
17
|
+
packageName: undefined,
|
|
16
18
|
ready: false,
|
|
17
19
|
userID: '',
|
|
18
20
|
permissions: 0,
|
|
@@ -26,42 +28,38 @@ const initialState: RookSyncGateState = {
|
|
|
26
28
|
const RookSyncGateProvider: FC<RookSyncGateProviderProps> = ({
|
|
27
29
|
environment,
|
|
28
30
|
clientUUID,
|
|
29
|
-
|
|
31
|
+
secret,
|
|
32
|
+
bundleId,
|
|
33
|
+
packageName,
|
|
30
34
|
children,
|
|
31
35
|
enableLogs = false,
|
|
32
36
|
enableBackgroundSync,
|
|
33
|
-
enableEventsBackgroundSync = true,
|
|
34
37
|
}) => {
|
|
35
38
|
const [state, dispatch] = useReducer(rookSyncGateReducer, initialState);
|
|
36
39
|
|
|
37
|
-
const { startBackgroundServices } = useRookAutoSync({
|
|
38
|
-
state,
|
|
39
|
-
enableBackground: enableBackgroundSync,
|
|
40
|
-
enableLogs,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
40
|
useEffect(() => {
|
|
44
41
|
initModule();
|
|
45
|
-
}, [environment, clientUUID,
|
|
42
|
+
}, [environment, clientUUID, secret]);
|
|
46
43
|
|
|
47
44
|
const initModule = async (): Promise<void> => {
|
|
48
45
|
dispatch({ type: 'SET_ROOK_ENVIRONMENT', environment });
|
|
49
46
|
dispatch({ type: 'SET_CLIENT_UUID', clientUUID });
|
|
50
|
-
dispatch({ type: '
|
|
47
|
+
dispatch({ type: 'SET_SECRET', secret });
|
|
48
|
+
dispatch({ type: 'SET_BUNDLE_ID', bundle: bundleId });
|
|
49
|
+
dispatch({ type: 'SET_PACKAGE_NAME', package: packageName });
|
|
51
50
|
dispatch({ type: 'SET_ENABLE_LOGS', enableLogs });
|
|
52
51
|
|
|
53
52
|
try {
|
|
54
53
|
await RookSdk.initRook({
|
|
55
54
|
environment,
|
|
56
55
|
clientUUID,
|
|
57
|
-
|
|
56
|
+
secret,
|
|
57
|
+
bundleId,
|
|
58
|
+
packageName,
|
|
58
59
|
enableLogs,
|
|
59
|
-
enableEventsBackgroundSync,
|
|
60
60
|
enableBackground: enableBackgroundSync,
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
if (enableBackgroundSync) startBackgroundServices();
|
|
64
|
-
|
|
65
63
|
dispatch({ type: 'SET_READY', ready: true });
|
|
66
64
|
} catch (error) {
|
|
67
65
|
console.log(error);
|
|
@@ -70,6 +68,12 @@ const RookSyncGateProvider: FC<RookSyncGateProviderProps> = ({
|
|
|
70
68
|
|
|
71
69
|
return (
|
|
72
70
|
<RookSyncGateContext.Provider value={{ state, dispatch }}>
|
|
71
|
+
<RookStateManager
|
|
72
|
+
state={state}
|
|
73
|
+
enableBackground={enableBackgroundSync}
|
|
74
|
+
enableLogs={enableLogs}
|
|
75
|
+
/>
|
|
76
|
+
|
|
73
77
|
{children}
|
|
74
78
|
</RookSyncGateContext.Provider>
|
|
75
79
|
);
|
|
@@ -26,8 +26,12 @@ export const rookSyncGateReducer = (
|
|
|
26
26
|
return { ...state, environment: action.environment };
|
|
27
27
|
case 'SET_CLIENT_UUID':
|
|
28
28
|
return { ...state, clientUUID: action.clientUUID };
|
|
29
|
-
case '
|
|
30
|
-
return { ...state,
|
|
29
|
+
case 'SET_SECRET':
|
|
30
|
+
return { ...state, secret: action.secret };
|
|
31
|
+
case 'SET_BUNDLE_ID':
|
|
32
|
+
return { ...state, bundleId: action.bundle };
|
|
33
|
+
case 'SET_PACKAGE_NAME':
|
|
34
|
+
return { ...state, packageName: action.package };
|
|
31
35
|
case 'SET_USER_ID':
|
|
32
36
|
return { ...state, userID: action.userID };
|
|
33
37
|
case 'SET_ENABLE_LOGS':
|
|
@@ -5,7 +5,9 @@ export type Environment = 'sandbox' | 'production';
|
|
|
5
5
|
type RookSyncGateState = {
|
|
6
6
|
clientUUID: string;
|
|
7
7
|
environment: Environment;
|
|
8
|
-
|
|
8
|
+
secret: string;
|
|
9
|
+
bundleId?: string;
|
|
10
|
+
packageName?: string;
|
|
9
11
|
ready: boolean;
|
|
10
12
|
userID: string;
|
|
11
13
|
permissions: number;
|
|
@@ -15,7 +17,9 @@ type RookSyncGateState = {
|
|
|
15
17
|
type Action =
|
|
16
18
|
| { type: 'SET_CLIENT_UUID'; clientUUID: string }
|
|
17
19
|
| { type: 'SET_ENABLE_LOGS'; enableLogs: boolean }
|
|
18
|
-
| { type: '
|
|
20
|
+
| { type: 'SET_SECRET'; secret: string }
|
|
21
|
+
| { type: 'SET_BUNDLE_ID'; bundle: string | undefined }
|
|
22
|
+
| { type: 'SET_PACKAGE_NAME'; package: string | undefined }
|
|
19
23
|
| { type: 'SET_PERMISSIONS' }
|
|
20
24
|
| { type: 'SET_READY'; ready: boolean }
|
|
21
25
|
| { type: 'SET_ROOK_ENVIRONMENT'; environment: Environment }
|
|
@@ -25,7 +29,9 @@ type RookSyncGateProviderProps = {
|
|
|
25
29
|
children: ReactNode;
|
|
26
30
|
environment: Environment;
|
|
27
31
|
clientUUID: string;
|
|
28
|
-
|
|
32
|
+
secret: string;
|
|
33
|
+
bundleId?: string;
|
|
34
|
+
packageName?: string;
|
|
29
35
|
enableLogs?: boolean;
|
|
30
36
|
enableBackgroundSync: boolean;
|
|
31
37
|
enableEventsBackgroundSync?: boolean;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import RookSdk from '../NativeRookSdk';
|
|
1
|
+
import RookSdk, { type DiagnosticState } from '../NativeRookSdk';
|
|
2
2
|
|
|
3
3
|
import { useRookSyncGateContext } from '../context/RookSyncGateContext';
|
|
4
4
|
import { isModuleReady } from '../utils/isModuleReady';
|
|
5
|
-
import {
|
|
5
|
+
import { SDKDataSource } from '../types/SDKSources';
|
|
6
|
+
import { Platform } from 'react-native';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* The `useRookConfiguration` hook provides various asynchronous methods for managing the configuration
|
|
@@ -72,11 +73,34 @@ export const useRookConfiguration = () => {
|
|
|
72
73
|
return RookSdk.removeUserFromRook(sources);
|
|
73
74
|
};
|
|
74
75
|
|
|
76
|
+
const getDiagnosticState = async (
|
|
77
|
+
source: SDKDataSource
|
|
78
|
+
): Promise<DiagnosticState> => {
|
|
79
|
+
isModuleReady(ready);
|
|
80
|
+
|
|
81
|
+
if (Platform.OS === 'android' && source === SDKDataSource.HEALTH_CONNECT) {
|
|
82
|
+
const value = await RookSdk.getHealthConnectDiagnosticState();
|
|
83
|
+
return JSON.parse(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (Platform.OS === 'android' && source === SDKDataSource.SAMSUNG_HEALTH) {
|
|
87
|
+
const value = await RookSdk.getSamsungHealthDiagnosticState();
|
|
88
|
+
return JSON.parse(value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (Platform.OS !== 'android' && source === SDKDataSource.APPLE_HEALTH) {
|
|
92
|
+
return RookSdk.getDiagnosticState();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
throw new Error('Check the datasource');
|
|
96
|
+
};
|
|
97
|
+
|
|
75
98
|
return {
|
|
76
99
|
ready,
|
|
77
100
|
getUserID,
|
|
78
101
|
updateUserID,
|
|
79
102
|
removeUserFromRook,
|
|
80
103
|
syncUserTimeZone,
|
|
104
|
+
getDiagnosticState,
|
|
81
105
|
};
|
|
82
106
|
};
|
package/src/hooks/useRookData.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import RookSdk from '../NativeRookSdk';
|
|
1
|
+
import RookSdk, { type WriteNutrition } from '../NativeRookSdk';
|
|
2
2
|
import { Platform } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { isModuleReady } from '../utils/isModuleReady';
|
|
5
|
-
import { isValidDate } from '../utils/isValidDate';
|
|
5
|
+
import { isValidDate, isValidZonedDateTime } from '../utils/isValidDate';
|
|
6
6
|
import { useRookSyncGateContext } from '../context/RookSyncGateContext';
|
|
7
7
|
import { SDKDataSource } from '../types/SDKSources';
|
|
8
8
|
import { type SleepSummary } from '../types/SleepSummary';
|
|
9
9
|
import { type PhysicalSummary } from '../types/PhysicalSummary';
|
|
10
10
|
import { type BodySummary } from '../types/BodySummary';
|
|
11
11
|
import { type ActivityEvent } from '../types/ActivityEvent';
|
|
12
|
-
import {
|
|
12
|
+
import { parseWriteNutritionToHC } from '../types/HCMealData';
|
|
13
|
+
import { parseWriteNutritionToAH } from '../types/AppleHealthNutritionEvent';
|
|
13
14
|
|
|
14
15
|
type Params = {
|
|
15
16
|
date: string;
|
|
@@ -101,11 +102,21 @@ export const useRookData = () => {
|
|
|
101
102
|
return RookSdk.getAppleHealthActivityEvents(date);
|
|
102
103
|
};
|
|
103
104
|
|
|
104
|
-
const
|
|
105
|
+
const writeNutritionData = async (
|
|
106
|
+
data: WriteNutrition,
|
|
105
107
|
source: SDKDataSource
|
|
106
|
-
): Promise<
|
|
108
|
+
): Promise<boolean> => {
|
|
109
|
+
isModuleReady(ready);
|
|
110
|
+
isValidZonedDateTime(data.date);
|
|
111
|
+
|
|
112
|
+
if (Platform.OS === 'android' && source === SDKDataSource.HEALTH_CONNECT) {
|
|
113
|
+
const event = parseWriteNutritionToHC(data);
|
|
114
|
+
return RookSdk.writeHealthConnectMealData(event);
|
|
115
|
+
}
|
|
116
|
+
|
|
107
117
|
if (Platform.OS !== 'android' && source === SDKDataSource.APPLE_HEALTH) {
|
|
108
|
-
|
|
118
|
+
const event = parseWriteNutritionToAH(data);
|
|
119
|
+
return RookSdk.writeAppleHealthMealData(event);
|
|
109
120
|
}
|
|
110
121
|
|
|
111
122
|
throw new Error('Please check the data source');
|
|
@@ -117,6 +128,6 @@ export const useRookData = () => {
|
|
|
117
128
|
getPhysicalSummary,
|
|
118
129
|
getBodySummary,
|
|
119
130
|
getActivityEvents,
|
|
120
|
-
|
|
131
|
+
writeNutritionData,
|
|
121
132
|
};
|
|
122
133
|
};
|
|
@@ -202,6 +202,20 @@ export const useRookPermissions = () => {
|
|
|
202
202
|
return RookSdk.revokeHealthConnectPermissions();
|
|
203
203
|
};
|
|
204
204
|
|
|
205
|
+
const requestAppleWriteNutritionPermission = async (): Promise<void> => {
|
|
206
|
+
isRunningOniOS();
|
|
207
|
+
isModuleReady(ready);
|
|
208
|
+
|
|
209
|
+
return RookSdk.requestAppleWriteNutritionPermission();
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const shouldRequestAndroidBackgroundPermissions = (): Promise<boolean> => {
|
|
213
|
+
isRunningOnAndroid();
|
|
214
|
+
isModuleReady(ready);
|
|
215
|
+
|
|
216
|
+
return RookSdk.shouldRequestAndroidBackgroundPermissions();
|
|
217
|
+
};
|
|
218
|
+
|
|
205
219
|
return {
|
|
206
220
|
ready,
|
|
207
221
|
androidHasBackgroundPermissions,
|
|
@@ -217,8 +231,10 @@ export const useRookPermissions = () => {
|
|
|
217
231
|
requestAppleHealthPermissions,
|
|
218
232
|
requestHealthConnectPermissions,
|
|
219
233
|
requestSamsungHealthPermissions,
|
|
234
|
+
requestAppleWriteNutritionPermission,
|
|
220
235
|
samsungHealthHasPartialPermissions,
|
|
221
236
|
samsungHealthHasPermissions,
|
|
222
237
|
revokeHealthConnectPermissions,
|
|
238
|
+
shouldRequestAndroidBackgroundPermissions,
|
|
223
239
|
};
|
|
224
240
|
};
|
package/src/hooks/useRookSync.ts
CHANGED
|
@@ -34,7 +34,7 @@ export const useRookSync = () => {
|
|
|
34
34
|
|
|
35
35
|
const syncWithOutParams = async (callback: Callback) => {
|
|
36
36
|
if (Platform.OS === 'android') {
|
|
37
|
-
const hcResult: any = await convertToPromise([
|
|
37
|
+
const hcResult: any = await convertToPromise([], RookSdk.sync);
|
|
38
38
|
|
|
39
39
|
RookSdk.shSync(enableLogs, (err: RookError, result: boolean) => {
|
|
40
40
|
callback({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import RookSdk from '../NativeRookSdk';
|
|
1
|
+
import RookSdk, { type HeartRateData } from '../NativeRookSdk';
|
|
2
2
|
import { Platform } from 'react-native';
|
|
3
3
|
import { isModuleReady } from '../utils/isModuleReady';
|
|
4
4
|
import { useRookSyncGateContext } from '../context/RookSyncGateContext';
|
|
@@ -69,9 +69,30 @@ export const useRookVariables = () => {
|
|
|
69
69
|
throw new Error('Invalid SDK datasource');
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
const getTodayHeartRate = async (
|
|
73
|
+
source: SDKDataSource
|
|
74
|
+
): Promise<HeartRateData> => {
|
|
75
|
+
if (Platform.OS === 'android' && source === SDKDataSource.HEALTH_CONNECT) {
|
|
76
|
+
const result = await RookSdk.getTodayHeartRate();
|
|
77
|
+
return JSON.parse(result as unknown as string);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (Platform.OS === 'android' && source === SDKDataSource.SAMSUNG_HEALTH) {
|
|
81
|
+
const result = await RookSdk.getSHTodayHeartRate();
|
|
82
|
+
return JSON.parse(result as unknown as string);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (Platform.OS !== 'android' && source === SDKDataSource.APPLE_HEALTH) {
|
|
86
|
+
return RookSdk.getTodayHeartRate();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw new Error('Please check the data source');
|
|
90
|
+
};
|
|
91
|
+
|
|
72
92
|
return {
|
|
73
93
|
ready,
|
|
74
94
|
getTodaySteps,
|
|
75
95
|
getTodayCalories,
|
|
96
|
+
getTodayHeartRate,
|
|
76
97
|
};
|
|
77
98
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
+
import { useEffect, useRef, type FC } from 'react';
|
|
3
|
+
import { AppState, type AppStateStatus } from 'react-native';
|
|
4
|
+
import type { RookSyncGateState } from '../../context/RookSyncGateTypes';
|
|
5
|
+
import { useRookPermissionEmitter } from '../hook/useRookEmitter';
|
|
6
|
+
import { useRookAutoSync } from '../hook/useRookAutoSync';
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
state: RookSyncGateState;
|
|
10
|
+
enableBackground: boolean;
|
|
11
|
+
enableLogs: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const RookStateManager: FC<Props> = (props) => {
|
|
15
|
+
const { checkAndroidPermissions } = useRookPermissionEmitter();
|
|
16
|
+
const { startBackgroundServices } = useRookAutoSync(props);
|
|
17
|
+
const appState = useRef(AppState.currentState);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const subscription = AppState.addEventListener(
|
|
21
|
+
'change',
|
|
22
|
+
(nextAppState: AppStateStatus) => {
|
|
23
|
+
if (
|
|
24
|
+
appState.current?.match(/inactive|background/) &&
|
|
25
|
+
nextAppState === 'active'
|
|
26
|
+
) {
|
|
27
|
+
checkAndroidPermissions();
|
|
28
|
+
startBackgroundServices();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
appState.current = nextAppState;
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
subscription.remove();
|
|
37
|
+
};
|
|
38
|
+
}, [checkAndroidPermissions]);
|
|
39
|
+
|
|
40
|
+
return <></>;
|
|
41
|
+
};
|
|
@@ -67,9 +67,11 @@ export const useRookAutoSync = ({
|
|
|
67
67
|
}, [state, changeAppState]);
|
|
68
68
|
|
|
69
69
|
const startBackgroundServices = () => {
|
|
70
|
-
const { clientUUID,
|
|
70
|
+
const { clientUUID, secret, ready } = state;
|
|
71
71
|
|
|
72
|
-
if (!
|
|
72
|
+
if (!enableBackground) return;
|
|
73
|
+
|
|
74
|
+
if (!ready || !clientUUID || !secret) {
|
|
73
75
|
return;
|
|
74
76
|
}
|
|
75
77
|
|
|
@@ -184,9 +186,9 @@ export const useRookAutoSync = ({
|
|
|
184
186
|
* broadcast with `value` set to `false` and an error message is logged.
|
|
185
187
|
*/
|
|
186
188
|
const startAndroidBackgroundSync = async () => {
|
|
187
|
-
const { clientUUID,
|
|
189
|
+
const { clientUUID, secret, ready } = state;
|
|
188
190
|
|
|
189
|
-
if (!ready || !clientUUID || !
|
|
191
|
+
if (!ready || !clientUUID || !secret) {
|
|
190
192
|
return;
|
|
191
193
|
}
|
|
192
194
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
+
import { useRef, useCallback } from 'react';
|
|
3
|
+
import RookSdk, { type BackgroundStatus } from '../../NativeRookSdk';
|
|
4
|
+
|
|
5
|
+
const PERM_KEYS = {
|
|
6
|
+
HC: 'ROOK_HEALTH_CONNECT_PERMISSIONS',
|
|
7
|
+
HC_PARTIAL: 'ROOK_HEALTH_CONNECT_PERMISSIONS_PARTIALLY_GRANTED',
|
|
8
|
+
HC_BG: 'ROOK_HEALTH_CONNECT_BACKGROUND_PERMISSIONS',
|
|
9
|
+
SAMSUNG: 'ROOK_SAMSUNG_HEALTH_PERMISSIONS',
|
|
10
|
+
SAMSUNG_PARTIAL: 'ROOK_SAMSUNG_HEALTH_PERMISSIONS_PARTIALLY_GRANTED',
|
|
11
|
+
ANDROID_BG: 'ROOK_BACKGROUND_ANDROID_PERMISSIONS',
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export const useRookPermissionEmitter = () => {
|
|
15
|
+
const emittedRef = useRef<Record<string, boolean>>({});
|
|
16
|
+
|
|
17
|
+
const evaluateAndEmit = useCallback(
|
|
18
|
+
async (key: string, checkFn: () => Promise<boolean | BackgroundStatus>) => {
|
|
19
|
+
let previuos = emittedRef.current[key] || false;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const result = await checkFn();
|
|
23
|
+
|
|
24
|
+
const isGranted =
|
|
25
|
+
typeof result === 'string' ? result === 'PERMISSION_GRANTED' : result;
|
|
26
|
+
|
|
27
|
+
if (previuos === isGranted) return;
|
|
28
|
+
|
|
29
|
+
const success = await RookSdk.sendMessage({
|
|
30
|
+
type: key,
|
|
31
|
+
value: isGranted,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (success) {
|
|
35
|
+
emittedRef.current[key] = true;
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(`Error checking/emitting ${key}:`, error);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
[]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const checkAndroidSystem = async () => {
|
|
45
|
+
await evaluateAndEmit(
|
|
46
|
+
PERM_KEYS.ANDROID_BG,
|
|
47
|
+
RookSdk.androidHasBackgroundPermissions
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const checkHealthConnect = async () => {
|
|
52
|
+
await Promise.all([
|
|
53
|
+
evaluateAndEmit(PERM_KEYS.HC, RookSdk.healthConnectHasPermissions),
|
|
54
|
+
evaluateAndEmit(
|
|
55
|
+
PERM_KEYS.HC_PARTIAL,
|
|
56
|
+
RookSdk.healthConnectHasPartialPermissions
|
|
57
|
+
),
|
|
58
|
+
evaluateAndEmit(
|
|
59
|
+
PERM_KEYS.HC_BG,
|
|
60
|
+
RookSdk.checkHealthConnectBackgroundReadStatus
|
|
61
|
+
),
|
|
62
|
+
]);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const checkSamsungHealth = async () => {
|
|
66
|
+
await Promise.all([
|
|
67
|
+
evaluateAndEmit(PERM_KEYS.SAMSUNG, RookSdk.samsungHealthHasPermissions),
|
|
68
|
+
evaluateAndEmit(
|
|
69
|
+
PERM_KEYS.SAMSUNG_PARTIAL,
|
|
70
|
+
RookSdk.samsungHealthHasPartialPermissions
|
|
71
|
+
),
|
|
72
|
+
]);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const checkAndroidPermissions = async () => {
|
|
76
|
+
try {
|
|
77
|
+
const samsungStatus = await RookSdk.checkSamsungAvailability();
|
|
78
|
+
const isSamsungAvailable = samsungStatus === 'INSTALLED';
|
|
79
|
+
|
|
80
|
+
checkAndroidSystem().then().catch(console.log);
|
|
81
|
+
checkHealthConnect().then().catch(console.log);
|
|
82
|
+
|
|
83
|
+
if (isSamsungAvailable) {
|
|
84
|
+
checkSamsungHealth().catch(console.log);
|
|
85
|
+
} else {
|
|
86
|
+
console.log(`Skipping Samsung Health checks. Status: ${samsungStatus}`);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error('Global Permission Check Failed', error);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return { checkAndroidPermissions };
|
|
94
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { WriteNutrition } from './WriteNutrition';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents the unit of measurement for nutrition.
|
|
5
|
+
* Map these integers to your SDK's unit requirements (e.g., Grams, Calories).
|
|
6
|
+
*/
|
|
7
|
+
export interface NutritionInsertionEventQuantity {
|
|
8
|
+
unit: number;
|
|
9
|
+
amount: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface NutritionInsertionEnergyWaterDataRelated {
|
|
13
|
+
dietaryKiloCaloriesEnergyConsumed?: number;
|
|
14
|
+
dietaryMilliLiterWater?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface NutritionCarbohydratesDataRelated {
|
|
18
|
+
dietaryGramCarbohydrates?: number;
|
|
19
|
+
dietaryGramFiber?: number;
|
|
20
|
+
dietaryGramSugar?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface NutritionFatsDataRelated {
|
|
24
|
+
dietaryGramFatTotal?: number;
|
|
25
|
+
dietaryGramFatSaturated?: number;
|
|
26
|
+
dietaryGramFatMonounsaturated?: number;
|
|
27
|
+
dietaryGramFatPolyunsaturated?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface NutritionProteinDataRelated {
|
|
31
|
+
dietaryGramProtein?: number;
|
|
32
|
+
dietaryMilliGramCholesterol?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface NutritionVitaminsDataRelated {
|
|
36
|
+
dietaryMicroGramVitaminA?: number;
|
|
37
|
+
dietaryMilliGramVitaminB6?: number;
|
|
38
|
+
dietaryMicroGramVitaminB12?: number;
|
|
39
|
+
dietaryMilliGramVitaminC?: number;
|
|
40
|
+
dietaryMicroGramVitaminD?: number;
|
|
41
|
+
dietaryMilliGramVitaminE?: number;
|
|
42
|
+
dietaryMicroGramVitaminK?: number;
|
|
43
|
+
dietaryMilliGramThiamin?: number;
|
|
44
|
+
dietaryMilliGramRiboflavin?: number;
|
|
45
|
+
dietaryMilliGramNiacin?: number;
|
|
46
|
+
dietaryMilliGramPantothenicAcid?: number;
|
|
47
|
+
dietaryMicroGramFolate?: number;
|
|
48
|
+
dietaryMicroGramBiotin?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface NutritionMineralDataRelated {
|
|
52
|
+
dietaryMilliGramsCalcium?: number;
|
|
53
|
+
dietaryMilliGramsChloride?: number;
|
|
54
|
+
dietaryMilliGramsChromium?: number;
|
|
55
|
+
dietaryMilliGramsCopper?: number;
|
|
56
|
+
dietaryMilliGramsIodine?: number;
|
|
57
|
+
dietaryMilliGramsIron?: number;
|
|
58
|
+
dietaryMilliGramsMagnesium?: number;
|
|
59
|
+
dietaryMilliGramsManganese?: number;
|
|
60
|
+
dietaryMilliGramsMolybdenum?: number;
|
|
61
|
+
dietaryMilliGramsPhosphorus?: number;
|
|
62
|
+
dietaryMilliGramsPotassium?: number;
|
|
63
|
+
dietaryMilliGramsSelenium?: number;
|
|
64
|
+
dietaryMilliGramsSodium?: number;
|
|
65
|
+
dietaryMilliGramsZinc?: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Main Interface for Apple Health Nutrition Insertion
|
|
70
|
+
*/
|
|
71
|
+
export interface AHNutritionInsertionEvent {
|
|
72
|
+
name: string;
|
|
73
|
+
quantity: NutritionInsertionEventQuantity;
|
|
74
|
+
/** ISO-8601 string (e.g., new Date().toISOString()) */
|
|
75
|
+
date: string;
|
|
76
|
+
energyWaterData?: NutritionInsertionEnergyWaterDataRelated;
|
|
77
|
+
carbohydratesData?: NutritionCarbohydratesDataRelated;
|
|
78
|
+
fatsData?: NutritionFatsDataRelated;
|
|
79
|
+
proteinData?: NutritionProteinDataRelated;
|
|
80
|
+
vitaminsData?: NutritionVitaminsDataRelated;
|
|
81
|
+
mineralData?: NutritionMineralDataRelated;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const parseWriteNutritionToAH = (
|
|
85
|
+
data: WriteNutrition
|
|
86
|
+
): AHNutritionInsertionEvent => {
|
|
87
|
+
return {
|
|
88
|
+
name: data.name,
|
|
89
|
+
date: data.date,
|
|
90
|
+
quantity: { unit: data.unit ?? 1, amount: data.amount ?? 0 },
|
|
91
|
+
energyWaterData: {
|
|
92
|
+
dietaryKiloCaloriesEnergyConsumed: data.calories,
|
|
93
|
+
dietaryMilliLiterWater: data.water,
|
|
94
|
+
},
|
|
95
|
+
carbohydratesData: {
|
|
96
|
+
dietaryGramCarbohydrates: data.totalCarbohydrates,
|
|
97
|
+
dietaryGramFiber: data.dietaryFiber,
|
|
98
|
+
dietaryGramSugar: data.sugar,
|
|
99
|
+
},
|
|
100
|
+
fatsData: {
|
|
101
|
+
dietaryGramFatTotal: data.totalFat,
|
|
102
|
+
dietaryGramFatSaturated: data.saturatedFat,
|
|
103
|
+
dietaryGramFatMonounsaturated: data.monounsaturatedFat,
|
|
104
|
+
dietaryGramFatPolyunsaturated: data.polyunsaturatedFat,
|
|
105
|
+
},
|
|
106
|
+
proteinData: {
|
|
107
|
+
dietaryGramProtein: data.protein,
|
|
108
|
+
dietaryMilliGramCholesterol: data.cholesterol,
|
|
109
|
+
},
|
|
110
|
+
vitaminsData: {
|
|
111
|
+
dietaryMicroGramVitaminA: data.vitaminA,
|
|
112
|
+
dietaryMilliGramVitaminB6: data.vitaminB6,
|
|
113
|
+
dietaryMicroGramVitaminB12: data.vitaminB12,
|
|
114
|
+
dietaryMilliGramVitaminC: data.vitaminC,
|
|
115
|
+
dietaryMicroGramVitaminD: data.vitaminD,
|
|
116
|
+
dietaryMilliGramVitaminE: data.vitaminE,
|
|
117
|
+
dietaryMicroGramVitaminK: data.vitaminK,
|
|
118
|
+
dietaryMilliGramThiamin: data.thiamin,
|
|
119
|
+
dietaryMilliGramRiboflavin: data.riboflavin,
|
|
120
|
+
dietaryMilliGramNiacin: data.niacin,
|
|
121
|
+
dietaryMilliGramPantothenicAcid: data.pantothenicAcid,
|
|
122
|
+
dietaryMicroGramFolate: data.folate,
|
|
123
|
+
dietaryMicroGramBiotin: data.biotin,
|
|
124
|
+
},
|
|
125
|
+
mineralData: {
|
|
126
|
+
dietaryMilliGramsCalcium: data.calcium,
|
|
127
|
+
dietaryMilliGramsChloride: data.chloride,
|
|
128
|
+
dietaryMilliGramsChromium: data.chromium,
|
|
129
|
+
dietaryMilliGramsCopper: data.copper,
|
|
130
|
+
dietaryMilliGramsIodine: data.iodine,
|
|
131
|
+
dietaryMilliGramsIron: data.iron,
|
|
132
|
+
dietaryMilliGramsMagnesium: data.magnesium,
|
|
133
|
+
dietaryMilliGramsManganese: data.manganese,
|
|
134
|
+
dietaryMilliGramsMolybdenum: data.molybdenum,
|
|
135
|
+
dietaryMilliGramsPhosphorus: data.phosphorus,
|
|
136
|
+
dietaryMilliGramsPotassium: data.potassium,
|
|
137
|
+
dietaryMilliGramsSelenium: data.selenium,
|
|
138
|
+
dietaryMilliGramsSodium: data.sodium,
|
|
139
|
+
dietaryMilliGramsZinc: data.zinc,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
};
|