diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 3902665..5e24e3a 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -37,5 +37,3 @@ newArchEnabled=true # Use this property to enable or disable the Hermes JS engine. # If set to false, you will be using JSC instead. hermesEnabled=true - -newArchEnabled=true \ No newline at end of file diff --git a/example/src/App.tsx b/example/src/App.tsx index ce692fc..98ee623 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -86,21 +86,23 @@ export default function App() { setSteps(data[0]?.value); }); write(HealthLinkDataType.BloodGlucose, { - value: 5, - }); - write(HealthLinkDataType.Height, { - value: 180, - }); - write(HealthLinkDataType.Weight, { - value: 80, + value: 6, }); write(HealthLinkDataType.Steps, { value: 100, - startDate: new Date('2024-12-31').toISOString(), + startDate: new Date('2024-12-30').toISOString(), endDate: new Date('2024-12-31').toISOString(), }); + write(HealthLinkDataType.Weight, { + value: 58, + unit: WeightUnit.Kg, + }); + write(HealthLinkDataType.Height, { + value: 165, + unit: HeighUnit.Cm, + }); write(HealthLinkDataType.HeartRate, { - value: 100, + value: 60, }); }); }, []); diff --git a/src/index.tsx b/src/index.tsx index f08be2b..f00f884 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -18,7 +18,7 @@ import { optionsToAndroidOptions, type ReadOptions, } from './types/dataTypes'; -import type { HealthInputOptions, HealthValue } from 'react-native-health'; +import type { HealthValue } from 'react-native-health'; import { readDataResultDeserializer, readIosCallback, @@ -68,17 +68,15 @@ export const write = async ( if (serializedData === null) { return; } - await writeIosCallback(dataType, serializedData as HealthInputOptions); + await writeIosCallback(dataType, serializedData as WriteOptions); } else if (Platform.OS === 'android') { const serializedData = serializeWriteOptions(dataType, data); + console.log([serializedData as HealthConnectRecord]); if (serializedData === null) { return; } - const a = await insertRecords([ - serializedData as HealthConnectRecord, - ]).catch((e) => { + await insertRecords([serializedData as HealthConnectRecord]).catch((e) => { console.error(e); }); - console.log(a); } }; diff --git a/src/types/dataTypes.ts b/src/types/dataTypes.ts index 33c154b..8a2c7f8 100644 --- a/src/types/dataTypes.ts +++ b/src/types/dataTypes.ts @@ -37,7 +37,7 @@ export const optionsToAndroidOptions = ( startTime: options.startDate!, endTime: options.endDate!, }, - ascendingOrder: options.ascending, + ascendingOrder: options.ascending ?? false, pageSize: options.limit, }; }; diff --git a/src/types/save.ts b/src/types/save.ts index bc280d0..3f5624f 100644 --- a/src/types/save.ts +++ b/src/types/save.ts @@ -1,11 +1,13 @@ -import type { HealthInputOptions, HealthUnit } from 'react-native-health'; +import { type HealthValueOptions } from 'react-native-health'; import { HealthLinkDataType } from './dataTypes'; import { Platform } from 'react-native'; import type { HealthConnectRecord } from 'react-native-health-connect'; import { - androidBloodGlucoseUnitMap, BloodGlucoseUnit, + HeartRateUnit, + HeighUnit, StepsUnit, + unitToIosUnitMap, WeightUnit, } from './units'; @@ -16,13 +18,18 @@ export interface WriteOptionsBase { ? { diastolic: number; systolic: number } : number; time?: string; - unit?: Partial; + unit?: Unit; metadata?: { source?: string; } & Record; } -export type Unit = BloodGlucoseUnit | WeightUnit | StepsUnit; +export type Unit = + | BloodGlucoseUnit + | WeightUnit + | StepsUnit + | HeighUnit + | HeartRateUnit; export type WriteOptions = WriteOptionsBase & (T extends HealthLinkDataType.Steps @@ -39,37 +46,104 @@ export type WriteDataType = export const serializeWriteOptions = ( dataType: T, options: WriteOptions -): HealthInputOptions | HealthConnectRecord | null => { +): HealthValueOptions | HealthConnectRecord | null => { if (Platform.OS === 'ios') { switch (dataType) { default: - return options; + let iosOptions: HealthValueOptions = { + unit: options.unit && unitToIosUnitMap[options.unit], + value: + options.unit === WeightUnit.Kg + ? (options.value ?? 0) * 1000 + : options.unit === HeighUnit.Cm + ? (options.value ?? 0) / 100 + : (options.value ?? 0), + }; + return iosOptions; } } else if (Platform.OS === 'android') { let androidOptions = { - recordType: dataType, startTime: options.startDate, endTime: options.endDate, }; switch (dataType) { case HealthLinkDataType.BloodGlucose: - const unit = options.unit - ? androidBloodGlucoseUnitMap[ - options.unit as unknown as BloodGlucoseUnit - ] - : 'millimolesPerLiter'; - //@ts-ignore return { ...androidOptions, + recordType: 'BloodGlucose', + relationToMeal: options.metadata?.relationToMeal ?? 0, + mealType: options.metadata?.mealType ?? 0, + specimenSource: options.metadata?.specimenSource ?? 0, + time: options.time ?? options.startDate ?? new Date().toISOString(), level: { - unit, - value: options.value, + unit: + options.unit === BloodGlucoseUnit.MgPerdL + ? 'milligramsPerDeciliter' + : 'millimolesPerLiter', + value: options.value as number, + }, + }; + case HealthLinkDataType.Steps: + return { + ...androidOptions, + recordType: 'Steps', + count: options.value ?? 0, + startTime: options.startDate ?? new Date().toISOString(), + endTime: options.endDate ?? new Date().toISOString(), + }; + case HealthLinkDataType.Weight: + return { + ...androidOptions, + recordType: 'Weight', + time: options.time ?? options.startDate ?? new Date().toISOString(), + weight: { + unit: + (options.unit as unknown as WeightUnit) === WeightUnit.Kg + ? 'kilograms' + : (options.unit as unknown as WeightUnit) === WeightUnit.Gram + ? 'grams' + : 'pounds', + value: options.value as number, }, }; + case HealthLinkDataType.Height: + return { + ...androidOptions, + recordType: 'Height', + time: options.time ?? options.startDate ?? new Date().toISOString(), + height: { + unit: + (options.unit as unknown as HeighUnit) === HeighUnit.Cm || + (options.unit as unknown as HeighUnit) === HeighUnit.Meter + ? 'meters' + : (options.unit as unknown as HeighUnit) === HeighUnit.Foot + ? 'feet' + : 'inches', + value: + (options.unit as unknown as HeighUnit) === HeighUnit.Cm + ? (options.value as number) / 100 + : (options.value as number), + }, + }; + case HealthLinkDataType.HeartRate: + return { + ...androidOptions, + recordType: 'HeartRate', + startTime: options.startDate ?? new Date().toISOString(), + endTime: options.endDate ?? new Date().toISOString(), + samples: [ + { + time: + options.time ?? options.startDate ?? new Date().toISOString(), + beatsPerMinute: options.value as number, + }, + ], + }; default: return null; } } + //@ts-ignore return options; }; diff --git a/src/types/units.ts b/src/types/units.ts index adcb3ba..d7ee340 100644 --- a/src/types/units.ts +++ b/src/types/units.ts @@ -1,3 +1,4 @@ +import { HealthUnit } from 'react-native-health'; import type { RecordResult } from 'react-native-health-connect'; export enum BloodGlucoseUnit { @@ -22,6 +23,10 @@ export enum StepsUnit { Count = 'count', } +export enum HeartRateUnit { + Bpm = 'bpm', +} + export const androidHeightUnitMap = ( data: RecordResult<'Height'>, unit?: string @@ -60,3 +65,17 @@ export const androidBloodGlucoseUnitMap = { [BloodGlucoseUnit.MgPerdL]: 'milligramsPerDeciliter', [BloodGlucoseUnit.MmolPerL]: 'millimolesPerLiter', }; + +export const unitToIosUnitMap = { + [HeighUnit.Meter]: HealthUnit.meter, + [HeighUnit.Foot]: HealthUnit.foot, + [HeighUnit.Inch]: HealthUnit.inch, + [WeightUnit.Gram]: HealthUnit.gram, + [WeightUnit.Pounds]: HealthUnit.pound, + [StepsUnit.Count]: HealthUnit.count, + [BloodGlucoseUnit.MgPerdL]: HealthUnit.mgPerdL, + [BloodGlucoseUnit.MmolPerL]: HealthUnit.mmolPerL, + [HeighUnit.Cm]: HealthUnit.meter, + [WeightUnit.Kg]: HealthUnit.gram, + [HeartRateUnit.Bpm]: HealthUnit.bpm, +};