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.
Files changed (99) hide show
  1. package/RNRookSdk.podspec +1 -1
  2. package/android/build.gradle +6 -0
  3. package/android/src/main/java/com/rooksdk/RookSdkModule.kt +1850 -6
  4. package/android/src/main/java/com/rooksdk/broadcasts/AndroidPermissionsReceiverTransmitter.kt +62 -0
  5. package/android/src/main/java/com/rooksdk/broadcasts/HealthConnectPermissionsReceiverTransmitter.kt +64 -0
  6. package/android/src/main/java/com/rooksdk/broadcasts/SamsungHealthPermissionsReceiverTransmitter.kt +44 -0
  7. package/android/src/main/java/com/rooksdk/utils/DatasourceUtils.kt +35 -0
  8. package/android/src/main/java/com/rooksdk/utils/PermissionConvertions.kt +21 -0
  9. package/android/src/main/java/com/rooksdk/utils/ReadableToWritable.kt +144 -0
  10. package/android/src/main/java/com/rooksdk/utils/RookDateTime.kt +26 -0
  11. package/android/src/main/java/com/rooksdk/utils/RookGsonBuilder.kt +20 -0
  12. package/android/src/main/java/com/rooksdk/utils/SamsungAvailability.kt +22 -0
  13. package/android/src/main/java/com/rooksdk/utils/Source.kt +6 -0
  14. package/android/src/main/java/com/rooksdk/utils/StringToSyncType.kt +41 -0
  15. package/android/src/main/java/com/rooksdk/utils/serializers/InstantSerializer.kt +18 -0
  16. package/android/src/main/java/com/rooksdk/utils/serializers/LocalDateSerializer.kt +21 -0
  17. package/android/src/main/java/com/rooksdk/utils/serializers/ZoneDateTimeSerializer.kt +18 -0
  18. package/ios/EncodableSDKState.swift +95 -0
  19. package/ios/NutritionMapper.swift +114 -0
  20. package/ios/RookConfiguration.swift +13 -0
  21. package/ios/RookData.swift +29 -0
  22. package/ios/RookEntry.swift +5 -4
  23. package/ios/RookPermissions.swift +33 -9
  24. package/ios/RookSdk.h +1 -13
  25. package/ios/RookSdk.mm +44 -237
  26. package/lib/module/NativeRookSdk.js +98 -10
  27. package/lib/module/NativeRookSdk.js.map +1 -1
  28. package/lib/module/context/RookSyncGateProvider.js +29 -21
  29. package/lib/module/context/RookSyncGateProvider.js.map +1 -1
  30. package/lib/module/context/RookSyncGateReducer.js +12 -2
  31. package/lib/module/context/RookSyncGateReducer.js.map +1 -1
  32. package/lib/module/hooks/useRookConfiguration.js +20 -1
  33. package/lib/module/hooks/useRookConfiguration.js.map +1 -1
  34. package/lib/module/hooks/useRookData.js +13 -4
  35. package/lib/module/hooks/useRookData.js.map +1 -1
  36. package/lib/module/hooks/useRookPermissions.js +13 -1
  37. package/lib/module/hooks/useRookPermissions.js.map +1 -1
  38. package/lib/module/hooks/useRookSync.js +1 -1
  39. package/lib/module/hooks/useRookSync.js.map +1 -1
  40. package/lib/module/hooks/useRookVariables.js +16 -1
  41. package/lib/module/hooks/useRookVariables.js.map +1 -1
  42. package/lib/module/modules/components/RookStateManager.js +31 -0
  43. package/lib/module/modules/components/RookStateManager.js.map +1 -0
  44. package/lib/module/modules/hook/useRookAutoSync.js +5 -4
  45. package/lib/module/modules/hook/useRookAutoSync.js.map +1 -1
  46. package/lib/module/modules/hook/useRookEmitter.js +61 -0
  47. package/lib/module/modules/hook/useRookEmitter.js.map +1 -0
  48. package/lib/module/types/AppleHealthNutritionEvent.js +72 -0
  49. package/lib/module/types/AppleHealthNutritionEvent.js.map +1 -0
  50. package/lib/module/types/HCMealData.js +61 -0
  51. package/lib/module/types/HCMealData.js.map +1 -0
  52. package/lib/module/types/WriteNutrition.js +14 -0
  53. package/lib/module/types/WriteNutrition.js.map +1 -0
  54. package/lib/module/utils/isValidDate.js +20 -0
  55. package/lib/module/utils/isValidDate.js.map +1 -1
  56. package/lib/typescript/src/NativeRookSdk.d.ts +622 -33
  57. package/lib/typescript/src/NativeRookSdk.d.ts.map +1 -1
  58. package/lib/typescript/src/context/RookSyncGateProvider.d.ts.map +1 -1
  59. package/lib/typescript/src/context/RookSyncGateReducer.d.ts.map +1 -1
  60. package/lib/typescript/src/context/RookSyncGateTypes.d.ts +14 -4
  61. package/lib/typescript/src/context/RookSyncGateTypes.d.ts.map +1 -1
  62. package/lib/typescript/src/hooks/useRookConfiguration.d.ts +3 -1
  63. package/lib/typescript/src/hooks/useRookConfiguration.d.ts.map +1 -1
  64. package/lib/typescript/src/hooks/useRookData.d.ts +2 -2
  65. package/lib/typescript/src/hooks/useRookData.d.ts.map +1 -1
  66. package/lib/typescript/src/hooks/useRookPermissions.d.ts +2 -0
  67. package/lib/typescript/src/hooks/useRookPermissions.d.ts.map +1 -1
  68. package/lib/typescript/src/hooks/useRookVariables.d.ts +2 -0
  69. package/lib/typescript/src/hooks/useRookVariables.d.ts.map +1 -1
  70. package/lib/typescript/src/modules/components/RookStateManager.d.ts +10 -0
  71. package/lib/typescript/src/modules/components/RookStateManager.d.ts.map +1 -0
  72. package/lib/typescript/src/modules/hook/useRookAutoSync.d.ts.map +1 -1
  73. package/lib/typescript/src/modules/hook/useRookEmitter.d.ts +4 -0
  74. package/lib/typescript/src/modules/hook/useRookEmitter.d.ts.map +1 -0
  75. package/lib/typescript/src/types/AppleHealthNutritionEvent.d.ts +76 -0
  76. package/lib/typescript/src/types/AppleHealthNutritionEvent.d.ts.map +1 -0
  77. package/lib/typescript/src/types/HCMealData.d.ts +60 -0
  78. package/lib/typescript/src/types/HCMealData.d.ts.map +1 -0
  79. package/lib/typescript/src/types/WriteNutrition.d.ts +61 -0
  80. package/lib/typescript/src/types/WriteNutrition.d.ts.map +1 -0
  81. package/lib/typescript/src/utils/isValidDate.d.ts +8 -0
  82. package/lib/typescript/src/utils/isValidDate.d.ts.map +1 -1
  83. package/package.json +1 -1
  84. package/src/NativeRookSdk.ts +682 -117
  85. package/src/context/RookSyncGateProvider.tsx +20 -16
  86. package/src/context/RookSyncGateReducer.ts +6 -2
  87. package/src/context/RookSyncGateTypes.ts +9 -3
  88. package/src/hooks/useRookConfiguration.ts +26 -2
  89. package/src/hooks/useRookData.ts +18 -7
  90. package/src/hooks/useRookPermissions.ts +16 -0
  91. package/src/hooks/useRookSync.ts +1 -1
  92. package/src/hooks/useRookVariables.ts +22 -1
  93. package/src/modules/components/RookStateManager.tsx +41 -0
  94. package/src/modules/hook/useRookAutoSync.ts +6 -4
  95. package/src/modules/hook/useRookEmitter.ts +94 -0
  96. package/src/types/AppleHealthNutritionEvent.ts +142 -0
  97. package/src/types/HCMealData.ts +114 -0
  98. package/src/types/WriteNutrition.ts +66 -0
  99. 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 { useRookAutoSync } from '../modules/hook/useRookAutoSync';
10
+ import { RookStateManager } from '../modules/components/RookStateManager';
11
11
 
12
12
  const initialState: RookSyncGateState = {
13
13
  clientUUID: '',
14
14
  environment: 'sandbox',
15
- password: '',
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
- password,
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, password]);
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: 'SET_PASSWORD', password });
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
- password,
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 'SET_PASSWORD':
30
- return { ...state, password: action.password };
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
- password: string;
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: 'SET_PASSWORD'; password: string }
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
- password: string;
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 { type SDKDataSource } from '../types/SDKSources';
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
  };
@@ -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 { type HeartRateData } from '../types/HeartRateEvent';
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 getTodayHeartRate = async (
105
+ const writeNutritionData = async (
106
+ data: WriteNutrition,
105
107
  source: SDKDataSource
106
- ): Promise<HeartRateData> => {
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
- return RookSdk.getTodayHeartRate() as unknown as HeartRateData;
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
- getTodayHeartRate,
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
  };
@@ -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([enableLogs], RookSdk.sync);
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, password, ready } = state;
70
+ const { clientUUID, secret, ready } = state;
71
71
 
72
- if (!ready || !clientUUID || !password) {
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, password, ready } = state;
189
+ const { clientUUID, secret, ready } = state;
188
190
 
189
- if (!ready || !clientUUID || !password) {
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
+ };