2025-02-24 22:27:01 -05:00
|
|
|
// app/(workout)/create.tsx
|
|
|
|
import React, { useState, useEffect } from 'react';
|
2025-03-05 14:37:38 -05:00
|
|
|
import { View, ScrollView, StyleSheet, TouchableOpacity } from 'react-native';
|
2025-02-26 23:30:00 -05:00
|
|
|
import { router, useNavigation } from 'expo-router';
|
2025-02-24 22:27:01 -05:00
|
|
|
import { TabScreen } from '@/components/layout/TabScreen';
|
|
|
|
import { Text } from '@/components/ui/text';
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
import {
|
|
|
|
AlertDialog,
|
|
|
|
AlertDialogAction,
|
|
|
|
AlertDialogContent,
|
|
|
|
AlertDialogDescription,
|
|
|
|
AlertDialogHeader,
|
2025-02-25 15:03:45 -05:00
|
|
|
AlertDialogTitle,
|
|
|
|
AlertDialogCancel
|
2025-02-24 22:27:01 -05:00
|
|
|
} from '@/components/ui/alert-dialog';
|
|
|
|
import { useWorkoutStore } from '@/stores/workoutStore';
|
2025-03-01 13:43:42 -05:00
|
|
|
import { Plus, Pause, Play, MoreHorizontal, Dumbbell, ChevronLeft } from 'lucide-react-native';
|
2025-02-24 22:27:01 -05:00
|
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
|
import EditableText from '@/components/EditableText';
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
import { generateId } from '@/utils/ids';
|
|
|
|
import { WorkoutSet } from '@/types/workout';
|
2025-02-25 15:03:45 -05:00
|
|
|
import { formatTime } from '@/utils/formatTime';
|
2025-02-26 23:30:00 -05:00
|
|
|
import { ParamListBase } from '@react-navigation/native';
|
|
|
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
2025-03-01 13:43:42 -05:00
|
|
|
import SetInput from '@/components/workout/SetInput';
|
2025-03-12 19:23:28 -04:00
|
|
|
import { useColorScheme } from '@/lib/theme/useColorScheme';
|
2025-03-07 12:38:21 -05:00
|
|
|
import { WorkoutAlertDialog } from '@/components/workout/WorkoutAlertDialog';
|
2025-03-12 19:23:28 -04:00
|
|
|
import { useIconColor } from '@/lib/theme/iconUtils';
|
2025-02-24 22:27:01 -05:00
|
|
|
|
|
|
|
export default function CreateWorkoutScreen() {
|
|
|
|
const {
|
|
|
|
status,
|
|
|
|
activeWorkout,
|
|
|
|
elapsedTime,
|
|
|
|
restTimer,
|
2025-02-25 15:03:45 -05:00
|
|
|
clearAutoSave,
|
|
|
|
isMinimized
|
2025-02-24 22:27:01 -05:00
|
|
|
} = useWorkoutStore();
|
|
|
|
|
|
|
|
const {
|
|
|
|
pauseWorkout,
|
|
|
|
resumeWorkout,
|
|
|
|
completeWorkout,
|
|
|
|
updateWorkoutTitle,
|
2025-02-25 15:03:45 -05:00
|
|
|
updateSet,
|
|
|
|
cancelWorkout,
|
|
|
|
minimizeWorkout,
|
|
|
|
maximizeWorkout
|
2025-02-24 22:27:01 -05:00
|
|
|
} = useWorkoutStore.getState();
|
|
|
|
|
2025-03-05 14:37:38 -05:00
|
|
|
// Get theme colors
|
|
|
|
const { isDarkColorScheme } = useColorScheme();
|
2025-03-12 19:23:28 -04:00
|
|
|
// Get icon utilities
|
|
|
|
const { getIconProps } = useIconColor();
|
2025-03-05 14:37:38 -05:00
|
|
|
|
|
|
|
// Create dynamic styles based on theme
|
|
|
|
const dynamicStyles = StyleSheet.create({
|
|
|
|
timerText: {
|
|
|
|
fontVariant: ['tabular-nums']
|
|
|
|
},
|
|
|
|
cardContainer: {
|
|
|
|
marginBottom: 24,
|
|
|
|
backgroundColor: isDarkColorScheme ? '#1F1F23' : 'white',
|
|
|
|
borderRadius: 8,
|
|
|
|
borderWidth: 1,
|
|
|
|
borderColor: isDarkColorScheme ? '#333' : '#eee',
|
|
|
|
shadowColor: '#000',
|
|
|
|
shadowOffset: { width: 0, height: 1 },
|
|
|
|
shadowOpacity: 0.2,
|
|
|
|
shadowRadius: 2,
|
|
|
|
elevation: 2,
|
|
|
|
},
|
|
|
|
cardHeader: {
|
|
|
|
padding: 16,
|
|
|
|
flexDirection: 'row',
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
borderBottomColor: isDarkColorScheme ? '#333' : '#eee'
|
|
|
|
},
|
|
|
|
cardTitle: {
|
|
|
|
fontSize: 18,
|
|
|
|
fontWeight: 'bold',
|
|
|
|
color: '#8B5CF6' // Purple is used in both themes
|
|
|
|
},
|
|
|
|
setsInfo: {
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
paddingVertical: 4
|
|
|
|
},
|
|
|
|
setsInfoText: {
|
|
|
|
fontSize: 14,
|
|
|
|
color: isDarkColorScheme ? '#999' : '#666'
|
|
|
|
},
|
|
|
|
headerRow: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
paddingVertical: 4,
|
|
|
|
borderTopWidth: 1,
|
|
|
|
borderTopColor: isDarkColorScheme ? '#333' : '#eee',
|
|
|
|
backgroundColor: isDarkColorScheme ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.03)'
|
|
|
|
},
|
|
|
|
headerCell: {
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: '500',
|
|
|
|
color: isDarkColorScheme ? '#999' : '#666',
|
|
|
|
textAlign: 'center'
|
|
|
|
},
|
|
|
|
setNumberCell: {
|
|
|
|
width: 32
|
|
|
|
},
|
|
|
|
prevCell: {
|
|
|
|
width: 80
|
|
|
|
},
|
|
|
|
valueCell: {
|
|
|
|
flex: 1
|
|
|
|
},
|
|
|
|
spacer: {
|
|
|
|
width: 44
|
|
|
|
},
|
|
|
|
setsList: {
|
|
|
|
padding: 0
|
|
|
|
},
|
|
|
|
actionButton: {
|
|
|
|
borderTopWidth: 1,
|
|
|
|
borderTopColor: isDarkColorScheme ? '#333' : '#eee'
|
|
|
|
},
|
|
|
|
iconColor: {
|
|
|
|
color: isDarkColorScheme ? '#999' : '#666'
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-02-26 23:30:00 -05:00
|
|
|
type CreateScreenNavigationProp = NativeStackNavigationProp<ParamListBase>;
|
|
|
|
const navigation = useNavigation<CreateScreenNavigationProp>();
|
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
// Check if we're coming from minimized state when component mounts
|
|
|
|
useEffect(() => {
|
|
|
|
if (isMinimized) {
|
|
|
|
maximizeWorkout();
|
|
|
|
}
|
2025-02-26 23:30:00 -05:00
|
|
|
}, [isMinimized, maximizeWorkout]);
|
|
|
|
|
|
|
|
// Handle back navigation
|
|
|
|
useEffect(() => {
|
|
|
|
const unsubscribe = navigation.addListener('beforeRemove', (e) => {
|
|
|
|
// If we have an active workout, just minimize it before continuing
|
|
|
|
if (activeWorkout && !isMinimized) {
|
|
|
|
// Call minimizeWorkout to update the state
|
|
|
|
minimizeWorkout();
|
|
|
|
|
|
|
|
// Let the navigation continue naturally
|
|
|
|
// Don't call router.back() here to avoid recursion
|
|
|
|
}
|
|
|
|
});
|
2025-02-25 15:03:45 -05:00
|
|
|
|
2025-02-26 23:30:00 -05:00
|
|
|
return unsubscribe;
|
|
|
|
}, [navigation, activeWorkout, isMinimized, minimizeWorkout]);
|
2025-02-25 15:03:45 -05:00
|
|
|
|
2025-03-07 12:38:21 -05:00
|
|
|
const [showFinishDialog, setShowFinishDialog] = useState(false);
|
2025-02-24 22:27:01 -05:00
|
|
|
const [showCancelDialog, setShowCancelDialog] = useState(false);
|
|
|
|
const insets = useSafeAreaInsets();
|
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
// Handler for confirming workout cancellation
|
|
|
|
const confirmCancelWorkout = async () => {
|
|
|
|
setShowCancelDialog(false);
|
2025-02-24 22:27:01 -05:00
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
// If cancelWorkout exists in the store, use it
|
|
|
|
if (typeof cancelWorkout === 'function') {
|
|
|
|
await cancelWorkout();
|
|
|
|
} else {
|
|
|
|
// Otherwise use the clearAutoSave function
|
|
|
|
await clearAutoSave();
|
2025-02-24 22:27:01 -05:00
|
|
|
}
|
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
router.back();
|
|
|
|
};
|
2025-02-24 22:27:01 -05:00
|
|
|
|
|
|
|
// Handler for adding a new set to an exercise
|
|
|
|
const handleAddSet = (exerciseIndex: number) => {
|
|
|
|
if (!activeWorkout) return;
|
|
|
|
|
|
|
|
const exercise = activeWorkout.exercises[exerciseIndex];
|
|
|
|
const lastSet = exercise.sets[exercise.sets.length - 1];
|
|
|
|
|
|
|
|
const newSet: WorkoutSet = {
|
|
|
|
id: generateId('local'),
|
|
|
|
weight: lastSet?.weight || 0,
|
|
|
|
reps: lastSet?.reps || 0,
|
|
|
|
type: 'normal',
|
|
|
|
isCompleted: false
|
|
|
|
};
|
|
|
|
|
|
|
|
updateSet(exerciseIndex, exercise.sets.length, newSet);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Show empty state when no workout is active
|
|
|
|
if (!activeWorkout) {
|
|
|
|
return (
|
|
|
|
<TabScreen>
|
|
|
|
<View className="flex-1 items-center justify-center p-6">
|
|
|
|
<Text className="text-xl font-semibold text-foreground text-center mb-4">
|
|
|
|
No active workout
|
|
|
|
</Text>
|
|
|
|
<Button
|
|
|
|
onPress={() => router.back()}
|
|
|
|
>
|
|
|
|
<Text className="text-primary-foreground">Go Back</Text>
|
|
|
|
</Button>
|
|
|
|
</View>
|
|
|
|
</TabScreen>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
// Show rest timer overlay when active
|
|
|
|
if (restTimer.isActive) {
|
|
|
|
return (
|
|
|
|
<TabScreen>
|
|
|
|
<View className="flex-1 items-center justify-center bg-background/80">
|
|
|
|
{/* Timer Display */}
|
|
|
|
<View className="items-center mb-8">
|
|
|
|
<Text className="text-4xl font-bold text-foreground mb-2">
|
|
|
|
Rest Timer
|
|
|
|
</Text>
|
|
|
|
<Text className="text-6xl font-bold text-primary">
|
|
|
|
{formatTime(restTimer.remaining * 1000)}
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* Controls */}
|
|
|
|
<View className="flex-row gap-4">
|
|
|
|
<Button
|
|
|
|
size="lg"
|
|
|
|
variant="outline"
|
|
|
|
onPress={() => useWorkoutStore.getState().stopRest()}
|
|
|
|
>
|
|
|
|
<Text>Skip</Text>
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
<Button
|
|
|
|
size="lg"
|
|
|
|
variant="outline"
|
|
|
|
onPress={() => useWorkoutStore.getState().extendRest(30)}
|
|
|
|
>
|
2025-03-05 14:37:38 -05:00
|
|
|
<View>
|
2025-03-12 19:23:28 -04:00
|
|
|
<Plus {...getIconProps('primary')} size={18} />
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
2025-02-25 15:03:45 -05:00
|
|
|
<Text>Add 30s</Text>
|
|
|
|
</Button>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
</TabScreen>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const hasExercises = activeWorkout.exercises.length > 0;
|
|
|
|
|
2025-02-24 22:27:01 -05:00
|
|
|
return (
|
|
|
|
<TabScreen>
|
|
|
|
<View style={{ flex: 1, paddingTop: insets.top }}>
|
2025-02-26 23:30:00 -05:00
|
|
|
{/* Header with back button */}
|
|
|
|
<View className="px-4 py-3 flex-row items-center justify-between border-b border-border">
|
|
|
|
<View className="flex-row items-center">
|
|
|
|
<Button
|
|
|
|
variant="ghost"
|
|
|
|
size="icon"
|
|
|
|
onPress={() => {
|
|
|
|
minimizeWorkout();
|
|
|
|
router.back();
|
|
|
|
}}
|
|
|
|
>
|
2025-03-05 14:37:38 -05:00
|
|
|
<View>
|
2025-03-12 19:23:28 -04:00
|
|
|
<ChevronLeft {...getIconProps('primary')} />
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
2025-02-26 23:30:00 -05:00
|
|
|
</Button>
|
|
|
|
<Text className="text-xl font-semibold ml-2">Back</Text>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
<Button
|
|
|
|
variant="purple"
|
|
|
|
className="px-4"
|
2025-03-07 12:38:21 -05:00
|
|
|
onPress={() => setShowFinishDialog(true)}
|
2025-02-26 23:30:00 -05:00
|
|
|
disabled={!hasExercises}
|
|
|
|
>
|
|
|
|
<Text className="text-white font-medium">Finish</Text>
|
|
|
|
</Button>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* Full-width workout title */}
|
|
|
|
<View className="px-4 py-3">
|
|
|
|
<EditableText
|
|
|
|
value={activeWorkout.title}
|
|
|
|
onChangeText={(newTitle) => updateWorkoutTitle(newTitle)}
|
|
|
|
placeholder="Workout Title"
|
|
|
|
textStyle={{
|
|
|
|
fontSize: 24,
|
|
|
|
fontWeight: '700',
|
|
|
|
}}
|
|
|
|
/>
|
2025-02-24 22:27:01 -05:00
|
|
|
</View>
|
|
|
|
|
2025-02-26 23:30:00 -05:00
|
|
|
{/* Timer Display */}
|
|
|
|
<View className="flex-row items-center px-4 pb-3 border-b border-border">
|
2025-03-05 14:37:38 -05:00
|
|
|
<Text style={dynamicStyles.timerText} className={cn(
|
2025-02-26 23:30:00 -05:00
|
|
|
"text-2xl font-mono",
|
|
|
|
status === 'paused' ? "text-muted-foreground" : "text-foreground"
|
|
|
|
)}>
|
|
|
|
{formatTime(elapsedTime)}
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
{status === 'active' ? (
|
2025-02-25 15:03:45 -05:00
|
|
|
<Button
|
|
|
|
variant="ghost"
|
2025-02-26 23:30:00 -05:00
|
|
|
size="icon"
|
|
|
|
className="ml-2"
|
|
|
|
onPress={pauseWorkout}
|
2025-02-25 15:03:45 -05:00
|
|
|
>
|
2025-03-05 14:37:38 -05:00
|
|
|
<View>
|
2025-03-12 19:23:28 -04:00
|
|
|
<Pause {...getIconProps('primary')} />
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
2025-02-25 15:03:45 -05:00
|
|
|
</Button>
|
2025-02-26 23:30:00 -05:00
|
|
|
) : (
|
2025-02-24 22:27:01 -05:00
|
|
|
<Button
|
2025-02-26 23:30:00 -05:00
|
|
|
variant="ghost"
|
|
|
|
size="icon"
|
|
|
|
className="ml-2"
|
|
|
|
onPress={resumeWorkout}
|
2025-02-24 22:27:01 -05:00
|
|
|
>
|
2025-03-05 14:37:38 -05:00
|
|
|
<View>
|
2025-03-12 19:23:28 -04:00
|
|
|
<Play {...getIconProps('primary')} />
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
2025-02-24 22:27:01 -05:00
|
|
|
</Button>
|
2025-02-26 23:30:00 -05:00
|
|
|
)}
|
2025-02-24 22:27:01 -05:00
|
|
|
</View>
|
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
{/* Content Area */}
|
2025-02-24 22:27:01 -05:00
|
|
|
<ScrollView
|
|
|
|
className="flex-1"
|
|
|
|
contentContainerStyle={{
|
|
|
|
paddingHorizontal: 16,
|
|
|
|
paddingBottom: insets.bottom + 20,
|
2025-02-25 15:03:45 -05:00
|
|
|
paddingTop: 16,
|
|
|
|
...(hasExercises ? {} : { flex: 1 })
|
2025-02-24 22:27:01 -05:00
|
|
|
}}
|
|
|
|
>
|
2025-02-25 15:03:45 -05:00
|
|
|
{hasExercises ? (
|
|
|
|
// Exercise List when exercises exist
|
|
|
|
<>
|
|
|
|
{activeWorkout.exercises.map((exercise, exerciseIndex) => (
|
2025-03-05 14:37:38 -05:00
|
|
|
<View key={exercise.id} style={dynamicStyles.cardContainer}>
|
2025-02-25 15:03:45 -05:00
|
|
|
{/* Exercise Header */}
|
2025-03-05 14:37:38 -05:00
|
|
|
<View style={dynamicStyles.cardHeader}>
|
|
|
|
<Text style={dynamicStyles.cardTitle}>
|
2025-02-25 15:03:45 -05:00
|
|
|
{exercise.title}
|
|
|
|
</Text>
|
2025-03-05 14:37:38 -05:00
|
|
|
<TouchableOpacity onPress={() => console.log('Open exercise options')}>
|
|
|
|
<View>
|
2025-03-12 19:23:28 -04:00
|
|
|
<MoreHorizontal {...getIconProps('muted')} size={20} />
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
2025-02-25 15:03:45 -05:00
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* Sets Info */}
|
2025-03-05 14:37:38 -05:00
|
|
|
<View style={dynamicStyles.setsInfo}>
|
|
|
|
<Text style={dynamicStyles.setsInfoText}>
|
2025-02-25 15:03:45 -05:00
|
|
|
{exercise.sets.filter(s => s.isCompleted).length} sets completed
|
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* Set Headers */}
|
2025-03-05 14:37:38 -05:00
|
|
|
<View style={dynamicStyles.headerRow}>
|
|
|
|
<Text style={[dynamicStyles.headerCell, dynamicStyles.setNumberCell]}>SET</Text>
|
|
|
|
<Text style={[dynamicStyles.headerCell, dynamicStyles.prevCell]}>PREV</Text>
|
|
|
|
<Text style={[dynamicStyles.headerCell, dynamicStyles.valueCell]}>KG</Text>
|
|
|
|
<Text style={[dynamicStyles.headerCell, dynamicStyles.valueCell]}>REPS</Text>
|
|
|
|
<View style={dynamicStyles.spacer} />
|
2025-02-25 15:03:45 -05:00
|
|
|
</View>
|
|
|
|
|
|
|
|
{/* Exercise Sets */}
|
2025-03-05 14:37:38 -05:00
|
|
|
<View style={dynamicStyles.setsList}>
|
2025-02-25 15:03:45 -05:00
|
|
|
{exercise.sets.map((set, setIndex) => {
|
2025-03-01 13:43:42 -05:00
|
|
|
const previousSet = setIndex > 0 ? exercise.sets[setIndex - 1] : undefined;
|
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
return (
|
2025-03-01 13:43:42 -05:00
|
|
|
<SetInput
|
2025-02-25 15:03:45 -05:00
|
|
|
key={set.id}
|
2025-03-01 13:43:42 -05:00
|
|
|
exerciseIndex={exerciseIndex}
|
|
|
|
setIndex={setIndex}
|
|
|
|
setNumber={setIndex + 1}
|
|
|
|
weight={set.weight}
|
|
|
|
reps={set.reps}
|
|
|
|
isCompleted={set.isCompleted}
|
|
|
|
previousSet={previousSet}
|
|
|
|
/>
|
2025-02-25 15:03:45 -05:00
|
|
|
);
|
|
|
|
})}
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
2025-02-25 15:03:45 -05:00
|
|
|
|
|
|
|
{/* Add Set Button */}
|
2025-03-05 14:37:38 -05:00
|
|
|
<View style={dynamicStyles.actionButton}>
|
|
|
|
<Button
|
|
|
|
variant="ghost"
|
|
|
|
className="flex-row justify-center items-center py-2"
|
|
|
|
onPress={() => handleAddSet(exerciseIndex)}
|
|
|
|
>
|
|
|
|
<View>
|
2025-03-12 19:23:28 -04:00
|
|
|
<Plus {...getIconProps('primary')} size={18} />
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
|
|
|
<Text className="text-foreground">Add Set</Text>
|
|
|
|
</Button>
|
|
|
|
</View>
|
|
|
|
</View>
|
2025-02-25 15:03:45 -05:00
|
|
|
))}
|
|
|
|
|
2025-02-26 23:30:00 -05:00
|
|
|
{/* Add Exercises Button */}
|
|
|
|
<Button
|
|
|
|
variant="purple"
|
|
|
|
className="w-full mb-4"
|
|
|
|
onPress={() => router.push('/(workout)/add-exercises')}
|
|
|
|
>
|
|
|
|
<Text className="text-white font-medium">Add Exercises</Text>
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
{/* Cancel Button */}
|
|
|
|
<Button
|
|
|
|
variant="outline"
|
|
|
|
className="w-full mb-8"
|
|
|
|
onPress={() => setShowCancelDialog(true)}
|
|
|
|
>
|
|
|
|
<Text className="text-foreground">Cancel Workout</Text>
|
|
|
|
</Button>
|
2025-02-25 15:03:45 -05:00
|
|
|
</>
|
2025-02-24 22:27:01 -05:00
|
|
|
) : (
|
2025-02-25 15:03:45 -05:00
|
|
|
// Empty State with nice message and icon
|
|
|
|
<View className="flex-1 justify-center items-center px-4">
|
2025-03-05 14:37:38 -05:00
|
|
|
<View>
|
2025-03-12 19:23:28 -04:00
|
|
|
<Dumbbell {...getIconProps('muted')} size={80} />
|
2025-03-05 14:37:38 -05:00
|
|
|
</View>
|
2025-02-25 15:03:45 -05:00
|
|
|
<Text className="text-xl font-semibold text-center mb-2">
|
|
|
|
No exercises added
|
2025-02-24 22:27:01 -05:00
|
|
|
</Text>
|
2025-02-25 15:03:45 -05:00
|
|
|
<Text className="text-base text-muted-foreground text-center mb-8">
|
|
|
|
Add exercises to start tracking your workout
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
{/* Add Exercises Button for empty state */}
|
|
|
|
<Button
|
|
|
|
variant="purple"
|
|
|
|
className="w-full mb-4"
|
|
|
|
onPress={() => router.push('/(workout)/add-exercises')}
|
|
|
|
>
|
|
|
|
<Text className="text-white font-medium">Add Exercises</Text>
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
{/* Cancel Button for empty state */}
|
|
|
|
<Button
|
|
|
|
variant="outline"
|
|
|
|
className="w-full"
|
|
|
|
onPress={() => setShowCancelDialog(true)}
|
|
|
|
>
|
|
|
|
<Text className="text-foreground">Cancel Workout</Text>
|
|
|
|
</Button>
|
2025-02-24 22:27:01 -05:00
|
|
|
</View>
|
|
|
|
)}
|
|
|
|
</ScrollView>
|
|
|
|
</View>
|
2025-02-25 15:03:45 -05:00
|
|
|
|
2025-03-07 12:38:21 -05:00
|
|
|
{/* Finish Workout Dialog */}
|
|
|
|
<WorkoutAlertDialog
|
|
|
|
open={showFinishDialog}
|
|
|
|
onOpenChange={setShowFinishDialog}
|
|
|
|
onConfirm={() => {
|
|
|
|
setShowFinishDialog(false);
|
|
|
|
// Set the end time before navigating
|
|
|
|
useWorkoutStore.setState(state => ({
|
|
|
|
activeWorkout: state.activeWorkout ? {
|
|
|
|
...state.activeWorkout,
|
|
|
|
endTime: Date.now(),
|
|
|
|
lastUpdated: Date.now()
|
|
|
|
} : null
|
|
|
|
}));
|
|
|
|
// Navigate to completion screen
|
|
|
|
router.push('/(workout)/complete');
|
|
|
|
}}
|
|
|
|
title="Complete Workout?"
|
|
|
|
description="Are you sure you want to finish this workout? This will end your current session."
|
|
|
|
confirmText="Complete Workout"
|
|
|
|
/>
|
|
|
|
|
2025-02-25 15:03:45 -05:00
|
|
|
{/* Cancel Workout Dialog */}
|
|
|
|
<AlertDialog open={showCancelDialog} onOpenChange={setShowCancelDialog}>
|
|
|
|
<AlertDialogContent>
|
|
|
|
<AlertDialogHeader>
|
2025-03-02 13:23:28 -05:00
|
|
|
<AlertDialogTitle>
|
|
|
|
<Text>Cancel Workout</Text>
|
|
|
|
</AlertDialogTitle>
|
2025-02-25 15:03:45 -05:00
|
|
|
<AlertDialogDescription>
|
|
|
|
<Text>Are you sure you want to cancel this workout? All progress will be lost.</Text>
|
|
|
|
</AlertDialogDescription>
|
|
|
|
</AlertDialogHeader>
|
2025-03-12 19:23:28 -04:00
|
|
|
<View className="flex-row justify-center gap-3 px-4 mt-2">
|
2025-02-25 15:03:45 -05:00
|
|
|
<AlertDialogCancel onPress={() => setShowCancelDialog(false)}>
|
|
|
|
<Text>Continue Workout</Text>
|
|
|
|
</AlertDialogCancel>
|
2025-03-12 19:23:28 -04:00
|
|
|
<AlertDialogAction onPress={confirmCancelWorkout} className='bg-destructive'>
|
|
|
|
<Text className='text-destructive-foreground'>Cancel Workout</Text>
|
2025-02-25 15:03:45 -05:00
|
|
|
</AlertDialogAction>
|
|
|
|
</View>
|
|
|
|
</AlertDialogContent>
|
|
|
|
</AlertDialog>
|
2025-02-24 22:27:01 -05:00
|
|
|
</TabScreen>
|
|
|
|
);
|
2025-02-26 23:30:00 -05:00
|
|
|
}
|