add home workout quick action with attendance check-in/out

This commit is contained in:
echo 2026-03-31 17:38:12 +02:00
parent 275248fc35
commit 2cff8eafbd

View File

@ -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<Attendance | null>(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() {
/>
<View style={styles.quickActionsGrid}>
<TouchableOpacity
onPress={() => 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,
},
]}
>
<View
@ -512,12 +587,16 @@ export default function HomeScreen() {
{ backgroundColor: "rgba(255,255,255,0.2)" },
]}
>
<Ionicons name="barbell" size={28} color={colors.white} />
<Ionicons
name={activeWorkoutSession ? "stop-circle" : "barbell"}
size={28}
color={colors.white}
/>
</View>
<Text
style={[typography.h4, { color: colors.white, marginTop: 12 }]}
>
Workout
{activeWorkoutSession ? "End Workout" : "Start Workout"}
</Text>
<Text
style={[
@ -525,7 +604,16 @@ export default function HomeScreen() {
{ color: "rgba(255,255,255,0.7)", marginTop: 4 },
]}
>
Log your session
{activeWorkoutSession
? `In session since ${new Date(
activeWorkoutSession.checkInTime,
).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}`
: workoutActionLoading
? "Updating session..."
: "Log your session"}
</Text>
</TouchableOpacity>