-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
341 additions
and
343 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
import { Platform } from 'react-native'; | ||
import { | ||
type ReadRecordsResult, | ||
type RecordResult, | ||
} from 'react-native-health-connect'; | ||
|
||
import type { HealthValue } from 'react-native-health'; | ||
import { HealthLinkDataType, type ReadOptions } from '../types/dataTypes'; | ||
import { | ||
androidHeightUnitMap, | ||
androidWeightUnitMap, | ||
BloodGlucoseUnit, | ||
} from '../types/units'; | ||
import type { HealthLinkDataValue } from '../types/results'; | ||
|
||
const AppleHealthKit = require('react-native-health'); | ||
|
||
export const dataValueDeserializer = <T extends HealthLinkDataType>( | ||
dataType: T, | ||
options: ReadOptions, | ||
dataValue: HealthValue | RecordResult<any> | ||
): HealthLinkDataValue<T> | null => { | ||
if (Platform.OS === 'ios') { | ||
let iosDataValue = dataValue as HealthValue; | ||
let result = { | ||
value: iosDataValue.value, | ||
id: iosDataValue.id, | ||
time: iosDataValue.startDate, | ||
metadata: { | ||
...iosDataValue.metadata, | ||
source: (iosDataValue as any).sourceId, | ||
}, | ||
}; | ||
|
||
switch (dataType) { | ||
case HealthLinkDataType.BloodPressure: | ||
return { | ||
...result, | ||
value: { | ||
systolic: (iosDataValue as any).bloodPressureSystolicValue, | ||
diastolic: (iosDataValue as any).bloodPressureDiastolicValue, | ||
}, | ||
} as HealthLinkDataValue<T>; | ||
case HealthLinkDataType.OxygenSaturation: | ||
return { | ||
...result, | ||
value: iosDataValue.value * 100, | ||
} as HealthLinkDataValue<T>; | ||
default: | ||
return result as HealthLinkDataValue<T>; | ||
} | ||
} else if (Platform.OS === 'android') { | ||
let androidDataValue = dataValue as RecordResult<any>; | ||
let result = { | ||
id: androidDataValue.metadata?.id, | ||
metadata: { | ||
...androidDataValue.metadata, | ||
source: androidDataValue.metadata?.dataOrigin as string, | ||
}, | ||
time: (androidDataValue as any).time, | ||
}; | ||
|
||
switch (dataType) { | ||
case HealthLinkDataType.BloodGlucose: { | ||
const bloodGlucoseDataValue = | ||
androidDataValue as RecordResult<'BloodGlucose'>; | ||
return { | ||
...result, | ||
metadata: { | ||
...result.metadata, | ||
relationToMeal: bloodGlucoseDataValue.relationToMeal, | ||
mealType: bloodGlucoseDataValue.mealType, | ||
specimenSource: bloodGlucoseDataValue.specimenSource, | ||
}, | ||
value: | ||
options?.unit === BloodGlucoseUnit.MgPerdL | ||
? bloodGlucoseDataValue.level?.inMilligramsPerDeciliter | ||
: bloodGlucoseDataValue.level?.inMillimolesPerLiter, | ||
} as unknown as HealthLinkDataValue<T>; | ||
} | ||
case HealthLinkDataType.BloodPressure: { | ||
const bloodPressureDataValue = | ||
androidDataValue as RecordResult<'BloodPressure'>; | ||
return { | ||
...result, | ||
value: { | ||
systolic: bloodPressureDataValue.systolic.inMillimetersOfMercury, | ||
diastolic: bloodPressureDataValue.diastolic.inMillimetersOfMercury, | ||
}, | ||
} as HealthLinkDataValue<T>; | ||
} | ||
case HealthLinkDataType.Height: { | ||
const heightDataValue = androidDataValue as RecordResult<'Height'>; | ||
return { | ||
...result, | ||
value: androidHeightUnitMap(heightDataValue, options.unit), | ||
} as HealthLinkDataValue<T>; | ||
} | ||
case HealthLinkDataType.Weight: { | ||
const weightDataValue = androidDataValue as RecordResult<'Weight'>; | ||
return { | ||
...result, | ||
value: androidWeightUnitMap(weightDataValue, options.unit), | ||
} as HealthLinkDataValue<T>; | ||
} | ||
case HealthLinkDataType.HeartRate: { | ||
const heartRateDataValue = | ||
androidDataValue as RecordResult<'HeartRate'>; | ||
return { | ||
...result, | ||
value: heartRateDataValue.samples[0]?.beatsPerMinute, | ||
time: heartRateDataValue.samples[0]?.time, | ||
} as HealthLinkDataValue<T>; | ||
} | ||
case HealthLinkDataType.RestingHeartRate: { | ||
const restingHeartRateDataValue = | ||
androidDataValue as RecordResult<'RestingHeartRate'>; | ||
return { | ||
...result, | ||
value: restingHeartRateDataValue.beatsPerMinute, | ||
} as HealthLinkDataValue<T>; | ||
} | ||
case HealthLinkDataType.OxygenSaturation: { | ||
const oxygenSaturationDataValue = | ||
androidDataValue as RecordResult<'OxygenSaturation'>; | ||
return { | ||
...result, | ||
value: oxygenSaturationDataValue.percentage, | ||
} as HealthLinkDataValue<T>; | ||
} | ||
case HealthLinkDataType.Steps: { | ||
const stepsDataValue = androidDataValue as RecordResult<'Steps'>; | ||
return { | ||
...result, | ||
value: stepsDataValue.count, | ||
} as HealthLinkDataValue<T>; | ||
} | ||
default: | ||
return result as HealthLinkDataValue<T>; | ||
} | ||
} | ||
return null; | ||
}; | ||
|
||
export const readDataResultDeserializer = <T extends HealthLinkDataType>( | ||
dataType: T, | ||
options: ReadOptions, | ||
data: ReadRecordsResult<T> | HealthValue[] | ||
): HealthLinkDataValue<T>[] => { | ||
let dataValueArray: RecordResult<T>[] | HealthValue[] = | ||
Platform.OS === 'ios' | ||
? (data as HealthValue[]) | ||
: (data as ReadRecordsResult<T>).records; | ||
return dataValueArray.reduce((acc, d) => { | ||
const deserializedValue = dataValueDeserializer(dataType, options, d); | ||
if (deserializedValue !== null) { | ||
acc.push(deserializedValue); | ||
} | ||
return acc; | ||
}, [] as HealthLinkDataValue<T>[]); | ||
}; | ||
|
||
export const readIosCallback = ( | ||
dataType: HealthLinkDataType, | ||
options: ReadOptions | ||
) => { | ||
return new Promise((resolve, reject) => { | ||
dataValueToIosReadFunction(dataType)(options, (err: any, results: any) => { | ||
if (err) { | ||
console.error(err); | ||
reject(err); | ||
} | ||
resolve(results); | ||
}); | ||
}); | ||
}; | ||
|
||
export const dataValueToIosReadFunction = (dataType: HealthLinkDataType) => { | ||
const dataTypeMap: { [key in HealthLinkDataType]?: any } = { | ||
BloodGlucose: AppleHealthKit.getBloodGlucoseSamples, | ||
Height: AppleHealthKit.getHeightSamples, | ||
Weight: AppleHealthKit.getWeightSamples, | ||
HeartRate: AppleHealthKit.getHeartRateSamples, | ||
RestingHeartRate: AppleHealthKit.getRestingHeartRateSamples, | ||
OxygenSaturation: AppleHealthKit.getOxygenSaturationSamples, | ||
BloodPressure: AppleHealthKit.getBloodPressureSamples, | ||
Steps: AppleHealthKit.getDailyStepCountSamples, | ||
}; | ||
return dataTypeMap[dataType] || (() => {}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import { type HealthValueOptions } from 'react-native-health'; | ||
import { HealthLinkDataType } from '../types/dataTypes'; | ||
import { Platform } from 'react-native'; | ||
import type { HealthConnectRecord } from 'react-native-health-connect'; | ||
import { | ||
BloodGlucoseUnit, | ||
HeighUnit, | ||
unitToIosUnitMap, | ||
WeightUnit, | ||
} from '../types/units'; | ||
import type { WriteDataType, WriteOptions } from '../types/save'; | ||
|
||
const AppleHealthKit = require('react-native-health'); | ||
|
||
export const serializeWriteOptions = <T extends WriteDataType>( | ||
dataType: T, | ||
options: WriteOptions<T> | ||
): HealthValueOptions | HealthConnectRecord | null => { | ||
if (Platform.OS === 'ios') { | ||
if (!options.unit) return null; | ||
const value = options.value ?? 0; | ||
const iosOptions: HealthValueOptions = { | ||
unit: unitToIosUnitMap[options.unit], | ||
value: | ||
options.unit === WeightUnit.Kg | ||
? value * 1000 | ||
: options.unit === HeighUnit.Cm | ||
? value / 100 | ||
: value, | ||
}; | ||
return iosOptions; | ||
} | ||
|
||
if (Platform.OS === 'android') { | ||
const androidOptions = { | ||
startTime: options.startDate ?? new Date().toISOString(), | ||
endTime: options.endDate ?? new Date().toISOString(), | ||
}; | ||
|
||
switch (dataType) { | ||
case HealthLinkDataType.BloodGlucose: { | ||
const { | ||
relationToMeal = 0, | ||
mealType = 0, | ||
specimenSource = 0, | ||
} = options.metadata || {}; | ||
const unit = | ||
options.unit === BloodGlucoseUnit.MgPerdL | ||
? 'milligramsPerDeciliter' | ||
: 'millimolesPerLiter'; | ||
return { | ||
...androidOptions, | ||
recordType: 'BloodGlucose', | ||
relationToMeal, | ||
mealType, | ||
specimenSource, | ||
time: options.time ?? androidOptions.startTime, | ||
level: { unit, value: options.value as number }, | ||
}; | ||
} | ||
case HealthLinkDataType.Steps: | ||
return { | ||
...androidOptions, | ||
recordType: 'Steps', | ||
count: options.value ?? 0, | ||
}; | ||
case HealthLinkDataType.Weight: { | ||
const weightUnit = | ||
options.unit === WeightUnit.Kg | ||
? 'kilograms' | ||
: options.unit === WeightUnit.Gram | ||
? 'grams' | ||
: 'pounds'; | ||
return { | ||
...androidOptions, | ||
recordType: 'Weight', | ||
time: options.time ?? androidOptions.startTime, | ||
weight: { unit: weightUnit, value: options.value as number }, | ||
}; | ||
} | ||
case HealthLinkDataType.Height: { | ||
const heightUnit = | ||
options.unit === HeighUnit.Cm || options.unit === HeighUnit.Meter | ||
? 'meters' | ||
: options.unit === HeighUnit.Foot | ||
? 'feet' | ||
: 'inches'; | ||
const heightValue = | ||
options.unit === HeighUnit.Cm | ||
? (options.value as number) / 100 | ||
: (options.value as number); | ||
return { | ||
...androidOptions, | ||
recordType: 'Height', | ||
time: options.time ?? androidOptions.startTime, | ||
height: { unit: heightUnit, value: heightValue }, | ||
}; | ||
} | ||
case HealthLinkDataType.HeartRate: | ||
return { | ||
...androidOptions, | ||
recordType: 'HeartRate', | ||
samples: [ | ||
{ | ||
time: options.time ?? androidOptions.startTime, | ||
beatsPerMinute: options.value as number, | ||
}, | ||
], | ||
}; | ||
default: | ||
return null; | ||
} | ||
} | ||
|
||
return null; | ||
}; | ||
|
||
export const writeIosCallback = <T extends WriteDataType>( | ||
dataType: WriteDataType, | ||
options: WriteOptions<T> | ||
) => { | ||
return new Promise((resolve, reject) => { | ||
dataValueToIosWriteFunction(dataType)(options, (err: any, results: any) => { | ||
if (err) { | ||
console.error(err); | ||
reject(err); | ||
} | ||
resolve(results); | ||
}); | ||
}); | ||
}; | ||
|
||
export const dataValueToIosWriteFunction = (dataType: WriteDataType) => { | ||
const dataTypeMap: { [key in WriteDataType]?: any } = { | ||
BloodGlucose: AppleHealthKit.saveBloodGlucoseSample, | ||
Height: AppleHealthKit.saveHeight, | ||
Weight: AppleHealthKit.saveWeight, | ||
HeartRate: AppleHealthKit.saveHeartRateSample, | ||
Steps: AppleHealthKit.saveSteps, | ||
}; | ||
return dataTypeMap[dataType] || (() => {}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.