From 1799af7436d3a51c93126f7756ade59d46f8da4d Mon Sep 17 00:00:00 2001 From: Joseph Bouqdib Date: Mon, 11 May 2026 19:24:32 +0100 Subject: [PATCH 01/14] fix: improve debug token handling in setupAppCheck function --- utils/initAppCheck.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/initAppCheck.ts b/utils/initAppCheck.ts index 93d6507..47683ae 100644 --- a/utils/initAppCheck.ts +++ b/utils/initAppCheck.ts @@ -8,7 +8,9 @@ import Constants from "expo-constants"; export async function setupAppCheck(): Promise { const extra = Constants.expoConfig?.extra; const isDevBuild = extra?.appVariant === "development"; - const debugToken: string | undefined = extra?.appCheckDebugToken ?? undefined; + const rawDebugToken = extra?.appCheckDebugToken; + const debugToken = + typeof rawDebugToken === "string" ? rawDebugToken : undefined; try { const rnfbProvider: ReactNativeFirebaseAppCheckProvider = From 26a90e88212a7148d372632978ebfad97eb433b1 Mon Sep 17 00:00:00 2001 From: Joseph Bouqdib Date: Mon, 11 May 2026 19:27:56 +0100 Subject: [PATCH 02/14] feat: implement superset functionality in workout sessions - Added superset handling in WorkoutSessionScreen to manage exercise transitions without rest. - Enhanced WorkoutCard to allow creation and removal of supersets for exercises. - Updated WorkoutDetailsScreen to visually indicate superset status for exercises. - Modified usePlanQuery to include supersetGroupId in workout records. - Extended activeWorkoutStore to manage superset logic during workout progression. - Implemented superset management in workoutStore for adding and removing exercises. - Updated database schema to support supersetGroupId in user_workout_exercises. - Ensured user data initialization includes supersetGroupId column if not present. --- app/(app)/(create-plan)/exercises.tsx | 66 ++++- app/(app)/(create-plan)/sets-overview.tsx | 42 ++- app/(app)/(workout)/index.tsx | 215 +++++++++++----- app/(app)/(workout)/workout-session.tsx | 57 +++- components/WorkoutCard.tsx | 301 +++++++++++++++++----- components/WorkoutDetailsScreen.tsx | 140 +++++++--- hooks/usePlanQuery.ts | 6 +- store/activeWorkoutStore.ts | 160 +++++++++++- store/workoutStore.ts | 131 ++++++++-- utils/database.ts | 35 ++- utils/initUserDataDB.ts | 12 + 11 files changed, 927 insertions(+), 238 deletions(-) diff --git a/app/(app)/(create-plan)/exercises.tsx b/app/(app)/(create-plan)/exercises.tsx index 5417d12..0a4a461 100644 --- a/app/(app)/(create-plan)/exercises.tsx +++ b/app/(app)/(create-plan)/exercises.tsx @@ -41,11 +41,19 @@ export default function ExercisesScreen() { newExerciseId, setNewExerciseId, replaceExercise, + createSuperset, } = useWorkoutStore(); - const { index } = useLocalSearchParams(); + const { index, supersetForIndex } = useLocalSearchParams<{ + index: string; + supersetForIndex?: string; + }>(); const currentWorkoutIndex = Number(index); const currentWorkout = workouts[currentWorkoutIndex]; const replacing = replaceExerciseIndex !== undefined; + const supersetMode = supersetForIndex !== undefined; + const supersetForExerciseIndex = supersetMode + ? Number(supersetForIndex) + : undefined; const { data: settings, @@ -67,7 +75,7 @@ export default function ExercisesScreen() { const [selectedExercises, setSelectedExercises] = useState([]); useEffect(() => { - if (currentWorkout?.exercises && !replacing) { + if (currentWorkout?.exercises && !replacing && !supersetMode) { const existingExerciseIds = currentWorkout.exercises.map( (exercise) => exercise.exercise_id, ); @@ -84,7 +92,13 @@ export default function ExercisesScreen() { // Clear the newExerciseId after it's used setNewExerciseId(null); } - }, [currentWorkout, newExerciseId, replacing, setNewExerciseId]); + }, [ + currentWorkout, + newExerciseId, + replacing, + supersetMode, + setNewExerciseId, + ]); const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; @@ -102,13 +116,38 @@ export default function ExercisesScreen() { const handleSelectExercise = useCallback( (exerciseId: number) => { - if (replacing) { - // Check if this exercise already exists in the current workout - const workout = workouts[currentWorkoutIndex]; - const exerciseAlreadyExists = workout.exercises.some( - (e) => e.exercise_id === exerciseId, - ); + const workout = workouts[currentWorkoutIndex]; + const exerciseAlreadyExists = workout?.exercises.some( + (e) => e.exercise_id === exerciseId, + ); + if (supersetMode) { + // Single-select: pair as superset partner + if (exerciseAlreadyExists) { + Alert.alert( + "Exercise Already Added", + "This exercise is already in your workout. Please choose a different one.", + [{ text: "OK" }], + ); + return; + } + const exercise = allExercises.find( + (ex) => ex.exercise_id === exerciseId, + ); + if (exercise && supersetForExerciseIndex !== undefined) { + const newExercise = { + ...exercise, + sets: + exercise.tracking_type === "time" ? defaultTimeSets : defaultSets, + }; + createSuperset( + currentWorkoutIndex, + supersetForExerciseIndex, + newExercise, + ); + router.back(); + } + } else if (replacing) { if (exerciseAlreadyExists) { Alert.alert( "Exercise Already Added", @@ -134,7 +173,7 @@ export default function ExercisesScreen() { defaultSets, defaultTimeSets, ); - router.back(); // Return immediately after replacement + router.back(); } } else { // Normal add mode - allow multiple selections @@ -146,14 +185,17 @@ export default function ExercisesScreen() { } }, [ + supersetMode, + supersetForExerciseIndex, replacing, allExercises, replaceExerciseIndex, defaultTimeSets, defaultSets, replaceExercise, + createSuperset, currentWorkoutIndex, - workouts, // Make sure 'workouts' is included in dependencies + workouts, ], ); @@ -280,7 +322,7 @@ export default function ExercisesScreen() { }); }} /> - {!replacing && ( + {!replacing && !supersetMode && (