import React, { memo, useEffect, useState } from "react"; import { ActivityIndicator, FlatList, StyleSheet, TouchableWithoutFeedback, View } from "react-native"; import { useSelector } from "react-redux"; import AppleHealthKitListItemView from "./AppleHealthKitListItemView"; import globalStyleDefinitions from "../../library/styles/globalStyleDefinitions"; import TitleText from "../atoms/text/TitleText"; import { colors } from "../../library/colors"; import Feather from 'react-native-vector-icons/Feather'; import { HealthKitPermissions } from 'react-native-health'; import { Platform } from "react-native"; import { getAccessToken } from "../../library/utils/asyncStoreManager"; import { apiHandler } from "../../api/apiHandler"; import { getDateFromTimestamp, getDateStringFormat } from "../../library/utils/globalFunctions"; interface AppleHealthKitViewProps { toDate: Date; formDate: Date; } const AppleHealthKitView: React.FC = ({ toDate, formDate }) => { const userDetails = useSelector((state: any) => state.memberState.userData); const [healthData, setHealthData] = useState>([]); const [isLoading, setIsLoading] = useState(true); useEffect(() => { fetchHealthData(); }, [userDetails, toDate, formDate]); function getDayISODateRangeFromISO(isoString: string) { // Parse the input date const date = new Date(isoString); // Create start of the day in local timezone const startDate = new Date( date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0 ); // Create end of the day in local timezone const endDate = new Date( date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999 ); return { startDateISO: startDate.toISOString(), endDateISO: endDate.toISOString(), }; } function sumSampleValues(samples: any) { if (!samples || samples.length === 0) return 0; // Some endpoints return an object instead of array, handle that: if (Array.isArray(samples)) { return samples.reduce((sum, item) => sum + (parseFloat(item.value) || 0), 0); } // If a single object with .value if (typeof samples === 'object' && samples.value !== undefined) { return parseFloat(samples.value) || 0; } return 0; } const fetchHealthData = async () => { if (!userDetails._id) { return; } try { setIsLoading(true); if (Platform.OS === 'android') { console.log("android"); } else if (Platform.OS === 'ios') { const appleHealthKitModule = await import('react-native-health'); const appleHealthKit = appleHealthKitModule.default; const permissions: HealthKitPermissions = { permissions: { read: [ appleHealthKit.Constants.Permissions.Steps, appleHealthKit.Constants.Permissions.ActiveEnergyBurned, appleHealthKit.Constants.Permissions.SleepAnalysis, appleHealthKit.Constants.Permissions.HeartRate, appleHealthKit.Constants.Permissions.BloodGlucose, appleHealthKit.Constants.Permissions.DistanceWalkingRunning, ], write: [], }, }; appleHealthKit.initHealthKit(permissions, (err) => { if (err) { console.error("Error initializing HealthKit: ", err); setIsLoading(false); return; } let objHealthData = [ { key: "Steps", value: 0 }, { key: "Calories", value: 0 }, { key: "Distance", value: 0 }, { key: "Sleep", value: 0 }, { key: "Heart Rate", value: 0 }, { key: "Glucose", value: 0 }, ]; const { startDateISO, endDateISO } = getDayISODateRangeFromISO(toDate.toISOString()); const fetchSleepAndBelow = () => { appleHealthKit.getSleepSamples({ startDate: startDateISO, endDate: endDateISO }, (err, sleepSamples) => { if (err) { console.error("SleepSamples error: ", err); } let totalSleepMs = 0; sleepSamples.forEach((sample: any) => { if (sample?.value == "INBED") { const start = new Date(sample.startDate); const end = new Date(sample.endDate); totalSleepMs += end.getTime() - start.getTime(); } }); // Convert ms to hours and minutes objHealthData[3].value = totalSleepMs / (1000 * 60 * 60); appleHealthKit.getHeartRateSamples({ startDate: startDateISO, endDate: endDateISO }, (err, heartRateSamples) => { if (err) { console.error("HeartRateSamples error: ", err); } objHealthData[4].value = Array.isArray(heartRateSamples) && heartRateSamples.length > 0 ? heartRateSamples[0]?.value : 0; appleHealthKit.getBloodGlucoseSamples({ startDate: startDateISO, endDate: endDateISO }, async (err, glucoseSamples) => { if (err) { console.error("BloodGlucoseSamples error: ", err); } objHealthData[5].value = Array.isArray(glucoseSamples) && glucoseSamples.length > 0 ? glucoseSamples[0]?.value : 0; setHealthData(objHealthData); let strTodayDate = getDateStringFormat(formDate); let reqObj = { date: strTodayDate, steps: parseFloat(objHealthData[0]?.value?.toFixed(2)) || 0, calories: parseFloat(objHealthData[1]?.value?.toFixed(2)) || 0, sleepDuration: parseFloat(objHealthData[3]?.value?.toFixed(2)) || 0, restHeartRate: parseFloat(objHealthData[4]?.value?.toFixed(2)) || 0, averageHeartRate: parseFloat(objHealthData[4]?.value?.toFixed(2)) || 0, glucoseLevel: parseFloat(objHealthData[5]?.value?.toFixed(2)) || 0, distance: parseFloat(objHealthData[2].value.toFixed(2)) || 0, }; const token = await getAccessToken(); const response = await apiHandler.addApplHealthData(token, reqObj); setIsLoading(false); }); }); }); }; // Steps (sample-based) appleHealthKit.getDailyStepCountSamples({ startDate: startDateISO, endDate: endDateISO }, (err, stepSamples) => { if (err) { console.error("DailyStepCountSamples error: ", err); setIsLoading(false); return; } objHealthData[0].value = sumSampleValues(stepSamples); // Calories (sample-based) appleHealthKit.getActiveEnergyBurned({ startDate: startDateISO, endDate: endDateISO }, (err, calorieSamples) => { if (err) { console.error("ActiveEnergyBurned error: ", err); } objHealthData[1].value = sumSampleValues(calorieSamples); // Distance (sample-based, fallback if getDistanceSamples not available: use getDistanceWalkingRunning) if (appleHealthKit.getDistanceSamples) { appleHealthKit.getDistanceSamples({ startDate: startDateISO, endDate: endDateISO }, (err, distanceSamples) => { if (err) { console.error("DistanceSamples error: ", err); } objHealthData[2].value = sumSampleValues(distanceSamples); // Sleep fetchSleepAndBelow(); }); } else { appleHealthKit.getDistanceWalkingRunning({ startDate: startDateISO, endDate: endDateISO }, (err, distance) => { if (err) { console.error("DistanceWalkingRunning error: ", err); } objHealthData[2].value = distance?.value || 0; fetchSleepAndBelow(); }); } }); }); }); } } catch (error) { console.error("Error in fetchHealthData: ", error); setIsLoading(false); } }; const renderSingleItem = ({ item, index }: any) => { return ; }; return ( <> {isLoading ? ( ) : ( )} ); }; export default memo(AppleHealthKitView); const styles = StyleSheet.create({ listHeading: { color: colors.black, paddingHorizontal: 8, }, headingContainer: { marginTop: globalStyleDefinitions.commonItemMargin.marginTop, justifyContent: 'space-between', flexDirection: 'row', alignItems: 'center' }, listContainer: { marginTop: globalStyleDefinitions.commonItemMargin.marginTop / 2, borderRadius: globalStyleDefinitions.b_r_6.borderRadius, overflow: "hidden", }, });