Skip to content

Commit

Permalink
fix: BMR calculation and health data loading
Browse files Browse the repository at this point in the history
  • Loading branch information
Th0rgal committed Dec 19, 2024
1 parent 139f795 commit 3654173
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 118 deletions.
14 changes: 9 additions & 5 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ const Tab = createBottomTabNavigator<MainTabParamList>();

export default function App() {
return (
<AuthProvider>
<OnboardingProvider>
<AppContent />
</OnboardingProvider>
</AuthProvider>
<HealthDataProvider>
<ApplicationSettingsProvider>
<OnboardingProvider>
<AuthProvider>
<AppContent />
</AuthProvider>
</OnboardingProvider>
</ApplicationSettingsProvider>
</HealthDataProvider>
);
}

Expand Down
12 changes: 7 additions & 5 deletions components/screens/onboarding/HealthPermissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import {
import { useOnboarding } from "../../../shared/OnboardingContext";
import AppleHealthKit, { HealthKitPermissions } from "react-native-health";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { useHealthData } from "../../../shared/HealthDataContext";

export default function HealthPermissions({ navigation }) {
const { setHasHealthPermissions } = useOnboarding();
const { initializeHealthKit } = useHealthData();
const scheme = useColorScheme();

const requestPermissions = async () => {
Expand Down Expand Up @@ -43,13 +45,13 @@ export default function HealthPermissions({ navigation }) {
},
} as HealthKitPermissions;

AppleHealthKit.initHealthKit(permissions, (error: string) => {
if (error) {
console.error("[ERROR] Cannot grant permissions:", error);
}
try {
await initializeHealthKit(permissions);
setHasHealthPermissions(true);
navigation.navigate("Login");
});
} catch (error) {
console.error("Error initializing HealthKit:", error);
}
};

return (
Expand Down
26 changes: 20 additions & 6 deletions components/screens/onboarding/LoadingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useColorScheme,
Animated,
Easing,
Platform,
} from "react-native";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { useHealthData } from "../../../shared/HealthDataContext";
Expand Down Expand Up @@ -58,18 +59,31 @@ export default function LoadingScreen({ navigation }) {

const initializeSettings = async () => {
try {
// Get or estimate BMR
let bmr = await estimateBMR();
if (!bmr || bmr < 800) {
bmr = 1800; // Default if no data available
}
console.log("Starting settings initialization...");

// Get current weight or use default
// Get current weight first
let currentWeight = await getCurrentWeight();
console.log("Current weight:", currentWeight);

if (!currentWeight) {
console.log("Using default weight as current weight was null");
currentWeight = 70; // Default weight in kg
}

// Now get or estimate BMR with weight data available
let bmr = await estimateBMR();
console.log("Estimated BMR:", bmr);

if (!bmr || bmr < 800) {
console.log(
"Using default BMR as estimated was:",
bmr,
"Reason:",
!bmr ? "null BMR" : "BMR too low"
);
bmr = 1800; // Default if no data available
}

// Set targets based on goals
let deficit = Math.round(bmr * 0.05); // Default 5% deficit
let surplus = Math.round(bmr * 0.05); // Default 5% surplus
Expand Down
13 changes: 11 additions & 2 deletions components/screens/onboarding/Subscription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,23 @@ export default function Subscription({
productId: info.entitlements.active.premium?.productIdentifier,
});

if (info.entitlements.active.premium) {
if (setIsSubscribed) {
await setIsSubscribed(true);
} else {
await setHasCompletedOnboarding(true);
}
return;
}

if (
info.entitlements.active !== undefined &&
Object.keys(info.entitlements.active).length > 0
) {
if (setIsSubscribed) {
setIsSubscribed(true);
await setIsSubscribed(true);
} else {
setHasCompletedOnboarding(true);
await setHasCompletedOnboarding(true);
}
}
} catch (error) {
Expand Down
196 changes: 96 additions & 100 deletions shared/HealthDataContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ import { Platform } from "react-native";
import AppleHealthKit, {
HealthValue,
HealthKitPermissions,
HealthUnit,
} from "react-native-health";

type HeightResponse = {
value: number;
startDate: string;
endDate: string;
};
type BiologicalSexResponse = { value: "female" | "male" };
type DateOfBirthResponse = { age: number };

type HealthDataContextType = {
weeklyActivity: {
value: number;
Expand All @@ -28,6 +35,7 @@ type HealthDataContextType = {
estimateBMR: () => Promise<number>;
saveFoodData: (options: Object) => void;
getCurrentWeight: () => Promise<number | null>;
initializeHealthKit: (permissions: HealthKitPermissions) => Promise<void>;
};

const defaultContext: HealthDataContextType = {
Expand Down Expand Up @@ -60,6 +68,7 @@ const defaultContext: HealthDataContextType = {
);
});
},
initializeHealthKit: async () => {},
};

export const HealthDataContext =
Expand All @@ -72,6 +81,7 @@ type HealthDataProviderProps = {
export const HealthDataProvider: React.FC<HealthDataProviderProps> = ({
children,
}) => {
const [isInitialized, setIsInitialized] = useState(true);
const [dailyActiveEnergyBurned, setDailyActiveEnergyBurned] =
useState<number>(0);
const [weeklyActivity, setWeeklyActivity] = useState<
Expand All @@ -85,41 +95,24 @@ export const HealthDataProvider: React.FC<HealthDataProviderProps> = ({
const [lastWeight, setLastWeight] = useState<number | null>(null);
const [lastBodyFat, setLastBodyFat] = useState<number | null>(null);

useEffect(() => {
initHealthKit();
}, []);

const initHealthKit = () => {
const permissions = {
permissions: {
read: [
AppleHealthKit.Constants.Permissions.ActiveEnergyBurned,
AppleHealthKit.Constants.Permissions.BodyFatPercentage,
AppleHealthKit.Constants.Permissions.Weight,
AppleHealthKit.Constants.Permissions.Height,
AppleHealthKit.Constants.Permissions.BiologicalSex,
AppleHealthKit.Constants.Permissions.DateOfBirth,
],
write: [
AppleHealthKit.Constants.Permissions.EnergyConsumed,
AppleHealthKit.Constants.Permissions.Protein,
AppleHealthKit.Constants.Permissions.Carbohydrates,
AppleHealthKit.Constants.Permissions.FatTotal,
],
},
} as HealthKitPermissions;

AppleHealthKit.initHealthKit(permissions, (error: string) => {
if (error) {
console.error("[ERROR] Cannot grant permissions:", error);
} else {
refreshActiveEnergyBurned();
fetchLatestWeight();
fetchLatestBodyFatPercentage();
}
const initializeHealthKit = async (permissions: HealthKitPermissions) => {
return new Promise<void>((resolve, reject) => {
AppleHealthKit.initHealthKit(permissions, (error: string) => {
if (error) {
console.error("[ERROR] Cannot grant permissions:", error);
reject(error);
} else {
setIsInitialized(true);
resolve();
}
});
});
};

if (!isInitialized) {
return null; // Or a loading indicator
}

const fetchLatestWeight = () => {
AppleHealthKit.getLatestWeight(
{ unit: AppleHealthKit.Constants.Units.gram },
Expand Down Expand Up @@ -179,83 +172,85 @@ export const HealthDataProvider: React.FC<HealthDataProviderProps> = ({
);
};

const estimateBMR = async () => {
// Check if lastWeight and lastBodyFat are available
if (lastWeight === null) {
console.error("Weight data is not available");
return null;
}
type HealthData = {
weight: number;
height: number;
biologicalSex: "male" | "female";
age: number;
};

let BMR = 0;
if (lastBodyFat !== null) {
// Katch-McArdle formula
const leanBodyMass = lastWeight * (1 - lastBodyFat / 100);
BMR = 370 + (21.6 * leanBodyMass) / 1000;
} else {
let height: number;
// Fetch height
try {
const heightResult: any = await new Promise((resolve, reject) => {
AppleHealthKit.getLatestHeight(null, (err, results) => {
if (err) {
reject("error getting latest height: " + err);
} else {
resolve(results);
const fetchHealthData = async (): Promise<HealthData | null> => {
try {
console.log("Fetching all health data...");
const [weight, height, biologicalSex, dateOfBirth] = await Promise.all([
new Promise<number>((resolve, reject) => {
AppleHealthKit.getLatestWeight(
{ unit: AppleHealthKit.Constants.Units.gram },
(err, result) => {
if (err) reject(err);
else resolve(result.value / 1000); // Convert to kg
}
);
}),
new Promise<number>((resolve, reject) => {
AppleHealthKit.getLatestHeight(null, (err, results) => {
if (err) reject(err);
else resolve(results.value * 2.54); // Convert to cm
});
});
height = heightResult.value * 2.54;
} catch (error) {
console.error(error);
return null;
}

let biologicalSex: "female" | "male";

// Fetch biological sex
try {
const biologicalSexResult: any = await new Promise(
(resolve, reject) => {
AppleHealthKit.getBiologicalSex(null, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
}
);
biologicalSex = biologicalSexResult.value;
} catch (error) {
console.error(error);
return null;
}

// Fetch age
let age: number;
try {
const ageResult: any = await new Promise((resolve, reject) => {
}),
new Promise<"male" | "female">((resolve, reject) => {
AppleHealthKit.getBiologicalSex(null, (err, results: HealthValue) => {
if (err) reject(err);
else resolve(results.value === 0 ? "female" : "male");
});
}),
new Promise<number>((resolve, reject) => {
AppleHealthKit.getDateOfBirth(null, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
if (err) reject(err);
else resolve(results.age);
});
});
age = ageResult.age;
} catch (error) {
console.error(error);
}),
]);

console.log("Health data fetched:", {
weight,
height,
biologicalSex,
age: dateOfBirth,
});
return { weight, height, biologicalSex, age: dateOfBirth };
} catch (error) {
console.error("Error fetching health data:", error);
return null;
}
};

const estimateBMR = async () => {
try {
const healthData = await fetchHealthData();
if (!healthData) {
console.error("Could not fetch required health data");
return null;
}

// Mifflin-St Jeor formula
const weightInKg = lastWeight / 1000;
const { weight, height, biologicalSex, age } = healthData;
const s = biologicalSex === "male" ? 5 : -161;
BMR = 10 * weightInKg + 6.25 * height - 5 * age + s;
}
const BMR = 10 * weight + 6.25 * height - 5 * age + s;

console.log("BMR Calculation details:", {
weight,
height,
biologicalSex,
age,
BMR,
formula: `10 * ${weight} + 6.25 * ${height} - 5 * ${age} + ${s}`,
});

return BMR;
return BMR;
} catch (error) {
console.error("Error calculating BMR:", error);
return null;
}
};

const saveFoodData = (options: Object) => {
Expand Down Expand Up @@ -298,6 +293,7 @@ export const HealthDataProvider: React.FC<HealthDataProviderProps> = ({
);
});
},
initializeHealthKit,
}}
>
{children}
Expand Down

0 comments on commit 3654173

Please sign in to comment.