From 2cff8eafbdf71f17c1e2e969f9e511cc79db3b58 Mon Sep 17 00:00:00 2001 From: echo Date: Tue, 31 Mar 2026 17:38:12 +0200 Subject: [PATCH] add home workout quick action with attendance check-in/out --- apps/mobile/src/app/(tabs)/index.tsx | 102 +++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 7 deletions(-) diff --git a/apps/mobile/src/app/(tabs)/index.tsx b/apps/mobile/src/app/(tabs)/index.tsx index cc1cdb1..553790c 100644 --- a/apps/mobile/src/app/(tabs)/index.tsx +++ b/apps/mobile/src/app/(tabs)/index.tsx @@ -10,7 +10,7 @@ import { Alert, AppState, } from "react-native"; -import { useUser } from "@clerk/clerk-expo"; +import { useAuth, useUser } from "@clerk/clerk-expo"; import { useState, useCallback, useEffect, useRef, useMemo } from "react"; import { useFocusEffect } from "@react-navigation/native"; import AsyncStorage from "@react-native-async-storage/async-storage"; @@ -27,6 +27,7 @@ import { AddWaterModal } from "../../components/AddWaterModal"; import { ScanFoodModal } from "../../components/ScanFoodModal"; import { ActivityRing } from "../../components/ActivityRing"; import { useMembership } from "../../hooks/useMembership"; +import { attendanceApi, type Attendance } from "../../api/attendance"; import { checkInsToActivities, completedGoalsToActivities, @@ -74,6 +75,7 @@ const getRandomMotivation = () => { export default function HomeScreen() { const { user } = useUser(); + const { getToken } = useAuth(); const { colors, typography } = useTheme(); const { features, membershipType } = useMembership(); const { refetchStatistics, forceRefresh, statistics, loading } = @@ -85,6 +87,9 @@ export default function HomeScreen() { const [scanFoodModalVisible, setScanFoodModalVisible] = useState(false); const [calories, setCalories] = useState(0); const [waterIntake, setWaterIntake] = useState(0); + const [activeWorkoutSession, setActiveWorkoutSession] = + useState(null); + const [workoutActionLoading, setWorkoutActionLoading] = useState(false); const [motivationalMessage, setMotivationalMessage] = useState( "Let's crush it today! 💪", ); @@ -181,14 +186,78 @@ export default function HomeScreen() { }, getMillisecondsUntilNextMidnight() + 50); }, [reconcileDailyMetrics]); + const fetchActiveWorkoutSession = useCallback(async () => { + try { + const token = await getToken(); + if (!token) { + setActiveWorkoutSession(null); + return; + } + + const history = await attendanceApi.getHistory(token); + if (history.length > 0 && !history[0].checkOutTime) { + setActiveWorkoutSession(history[0]); + } else { + setActiveWorkoutSession(null); + } + } catch { + setActiveWorkoutSession(null); + } + }, [getToken]); + useFocusEffect( useCallback(() => { void reconcileDailyMetrics(); + void fetchActiveWorkoutSession(); refetchStatistics(); refetchGoals(); - }, [reconcileDailyMetrics, refetchStatistics, refetchGoals]), + }, [ + fetchActiveWorkoutSession, + reconcileDailyMetrics, + refetchStatistics, + refetchGoals, + ]), ); + const handleWorkoutAction = useCallback(async () => { + try { + setWorkoutActionLoading(true); + const token = await getToken(); + if (!token) { + Alert.alert("Sign in required", "Please sign in to log your workout."); + return; + } + + if (activeWorkoutSession) { + await attendanceApi.checkOut(token); + Alert.alert("Workout logged", "Session ended successfully."); + } else { + await attendanceApi.checkIn("gym", token); + Alert.alert("Workout started", "Session started successfully."); + } + + await Promise.all([ + fetchActiveWorkoutSession(), + forceRefresh(), + refetchStatistics(), + ]); + } catch (error: unknown) { + const message = + error instanceof Error + ? error.message + : "Unable to update workout session. Please try again."; + Alert.alert("Workout action failed", message); + } finally { + setWorkoutActionLoading(false); + } + }, [ + activeWorkoutSession, + fetchActiveWorkoutSession, + forceRefresh, + getToken, + refetchStatistics, + ]); + const onRefresh = useCallback(async () => { setRefreshing(true); await Promise.all([forceRefresh(), refetchGoals()]); @@ -499,11 +568,17 @@ export default function HomeScreen() { /> console.log("Log workout")} + onPress={handleWorkoutAction} + disabled={workoutActionLoading} activeOpacity={0.85} style={[ styles.quickActionCard, - { backgroundColor: colors.primary }, + { + backgroundColor: activeWorkoutSession + ? colors.warning + : colors.primary, + opacity: workoutActionLoading ? 0.7 : 1, + }, ]} > - + - Workout + {activeWorkoutSession ? "End Workout" : "Start Workout"} - Log your session + {activeWorkoutSession + ? `In session since ${new Date( + activeWorkoutSession.checkInTime, + ).toLocaleTimeString([], { + hour: "2-digit", + minute: "2-digit", + })}` + : workoutActionLoading + ? "Updating session..." + : "Log your session"}