android icon and other bug fixes

This commit is contained in:
DocNR 2025-03-12 19:23:28 -04:00
parent 43d6d7d12b
commit 43df1aeb79
30 changed files with 286 additions and 156 deletions

View File

@ -14,7 +14,7 @@ export default function HistoryLayout() {
return (
<TabScreen>
<Header title="History" useLogo={true} />
<Header useLogo={true} showNotifications={true} />
<Tab.Navigator
initialRouteName="history"

View File

@ -20,6 +20,7 @@ import { Text } from '@/components/ui/text'
import { getRandomWorkoutTitle } from '@/utils/workoutTitles'
import { Bell, Star, Clock, Dumbbell } from 'lucide-react-native';
import { Button } from '@/components/ui/button';
import { useIconColor } from '@/lib/theme/iconUtils';
interface FavoriteTemplateData {
id: string;
@ -62,6 +63,7 @@ export default function WorkoutScreen() {
} = useWorkoutStore();
const theme = useTheme();
const { getIconProps } = useIconColor();
useFocusEffect(
useCallback(() => {
@ -232,24 +234,7 @@ export default function WorkoutScreen() {
return (
<TabScreen>
<Header
useLogo={true}
rightElement={
<Button
variant="ghost"
size="icon"
onPress={() => console.log('Open notifications')}
>
<View className="relative">
<Bell size={24} color={Platform.select({
ios: undefined,
android: '#8B5CF6'
})} />
<View className="absolute -top-1 -right-1 w-2 h-2 bg-primary rounded-full" />
</View>
</Button>
}
/>
<Header useLogo={true} showNotifications={true} />
<ScrollView
className="flex-1 px-4 pt-4"

View File

@ -16,7 +16,7 @@ export default function LibraryLayout() {
return (
<TabScreen>
<Header useLogo={true} />
<Header useLogo={true} showNotifications={true} />
<Tab.Navigator
initialRouteName="templates"

View File

@ -94,8 +94,11 @@ export default function TemplatesScreen() {
const handleStartWorkout = async (template: Template) => {
try {
// Use the workoutStore action to start a workout from template
await useWorkoutStore.getState().startWorkoutFromTemplate(template.id);
// Convert to WorkoutTemplate format
const workoutTemplate = toWorkoutTemplate(template);
// Start the workout
await useWorkoutStore.getState().startWorkoutFromTemplate(template.id, workoutTemplate);
// Navigate to the active workout screen
router.push('/(workout)/create');
@ -285,7 +288,7 @@ export default function TemplatesScreen() {
) : (
<View className="px-4">
<Text className="text-muted-foreground">
No templates found. Create one by clicking the + button.
So empty! Create a new workout template by clicking the + button.
</Text>
</View>
)}

View File

@ -76,7 +76,7 @@ export default function ProfileScreen() {
onPress={() => console.log('Open notifications')}
>
<View className="relative">
<Bell className="text-foreground" />
<Bell size={24} className="text-primary" />
<View className="absolute -top-1 -right-1 w-2 h-2 bg-primary rounded-full" />
</View>
</Button>
@ -115,21 +115,7 @@ export default function ProfileScreen() {
return (
<TabScreen>
<Header
useLogo={true}
rightElement={
<Button
variant="ghost"
size="icon"
onPress={() => console.log('Open notifications')}
>
<View className="relative">
<Bell className="text-foreground" />
<View className="absolute -top-1 -right-1 w-2 h-2 bg-primary rounded-full" />
</View>
</Button>
}
/>
<Header useLogo={true} showNotifications={true} />
<ScrollView
className="flex-1"

View File

@ -16,7 +16,7 @@ export default function SocialLayout() {
return (
<TabScreen>
<Header useLogo={true} />
<Header useLogo={true} showNotifications={true} />
<Tab.Navigator
initialRouteName="powr"

View File

@ -7,7 +7,7 @@ import { X } from 'lucide-react-native';
import { useWorkoutStore } from '@/stores/workoutStore';
import { WorkoutCompletionFlow } from '@/components/workout/WorkoutCompletionFlow';
import { WorkoutCompletionOptions } from '@/types/workout';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
/**
* Workout Completion Screen

View File

@ -25,8 +25,9 @@ import { formatTime } from '@/utils/formatTime';
import { ParamListBase } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import SetInput from '@/components/workout/SetInput';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import { WorkoutAlertDialog } from '@/components/workout/WorkoutAlertDialog';
import { useIconColor } from '@/lib/theme/iconUtils';
export default function CreateWorkoutScreen() {
const {
@ -51,6 +52,8 @@ export default function CreateWorkoutScreen() {
// Get theme colors
const { isDarkColorScheme } = useColorScheme();
// Get icon utilities
const { getIconProps } = useIconColor();
// Create dynamic styles based on theme
const dynamicStyles = StyleSheet.create({
@ -239,7 +242,7 @@ export default function CreateWorkoutScreen() {
onPress={() => useWorkoutStore.getState().extendRest(30)}
>
<View>
<Plus className="mr-2 text-foreground" size={18} />
<Plus {...getIconProps('primary')} size={18} />
</View>
<Text>Add 30s</Text>
</Button>
@ -266,7 +269,7 @@ export default function CreateWorkoutScreen() {
}}
>
<View>
<ChevronLeft className="text-foreground" />
<ChevronLeft {...getIconProps('primary')} />
</View>
</Button>
<Text className="text-xl font-semibold ml-2">Back</Text>
@ -312,7 +315,7 @@ export default function CreateWorkoutScreen() {
onPress={pauseWorkout}
>
<View>
<Pause className="text-foreground" />
<Pause {...getIconProps('primary')} />
</View>
</Button>
) : (
@ -323,7 +326,7 @@ export default function CreateWorkoutScreen() {
onPress={resumeWorkout}
>
<View>
<Play className="text-foreground" />
<Play {...getIconProps('primary')} />
</View>
</Button>
)}
@ -351,7 +354,7 @@ export default function CreateWorkoutScreen() {
</Text>
<TouchableOpacity onPress={() => console.log('Open exercise options')}>
<View>
<MoreHorizontal size={20} color={isDarkColorScheme ? "#999" : "#666"} />
<MoreHorizontal {...getIconProps('muted')} size={20} />
</View>
</TouchableOpacity>
</View>
@ -400,7 +403,7 @@ export default function CreateWorkoutScreen() {
onPress={() => handleAddSet(exerciseIndex)}
>
<View>
<Plus size={18} className="text-foreground mr-2" />
<Plus {...getIconProps('primary')} size={18} />
</View>
<Text className="text-foreground">Add Set</Text>
</Button>
@ -430,7 +433,7 @@ export default function CreateWorkoutScreen() {
// Empty State with nice message and icon
<View className="flex-1 justify-center items-center px-4">
<View>
<Dumbbell className="text-muted-foreground mb-6" size={80} />
<Dumbbell {...getIconProps('muted')} size={80} />
</View>
<Text className="text-xl font-semibold text-center mb-2">
No exercises added
@ -494,12 +497,12 @@ export default function CreateWorkoutScreen() {
<Text>Are you sure you want to cancel this workout? All progress will be lost.</Text>
</AlertDialogDescription>
</AlertDialogHeader>
<View className="flex-row justify-end gap-3">
<View className="flex-row justify-center gap-3 px-4 mt-2">
<AlertDialogCancel onPress={() => setShowCancelDialog(false)}>
<Text>Continue Workout</Text>
</AlertDialogCancel>
<AlertDialogAction onPress={confirmCancelWorkout}>
<Text>Cancel Workout</Text>
<AlertDialogAction onPress={confirmCancelWorkout} className='bg-destructive'>
<Text className='text-destructive-foreground'>Cancel Workout</Text>
</AlertDialogAction>
</View>
</AlertDialogContent>

View File

@ -6,8 +6,8 @@ import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import * as React from 'react';
import { View, Text, Platform } from 'react-native';
import { NAV_THEME } from '@/lib/constants';
import { useColorScheme } from '@/lib/useColorScheme';
import { NAV_THEME } from '@/lib/theme/constants';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import { PortalHost } from '@rn-primitives/portal';
import { setAndroidNavigationBar } from '@/lib/android-navigation-bar';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

View File

@ -12,7 +12,8 @@ import {
import { Text } from '@/components/ui/text';
import { Check, Edit2 } from 'lucide-react-native';
import { cn } from '@/lib/utils';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import { useIconColor } from '@/lib/theme/iconUtils';
interface EditableTextProps {
value: string;
@ -37,6 +38,7 @@ export default function EditableText({
const [tempValue, setTempValue] = useState(value);
const inputRef = useRef<TextInput>(null);
const { isDarkColorScheme } = useColorScheme();
const { getIconProps } = useIconColor();
const handleSubmit = () => {
if (tempValue.trim()) {
@ -72,7 +74,10 @@ export default function EditableText({
onPress={handleSubmit}
className="p-2 ml-2"
>
<Check className="text-primary" size={20} />
<Check
size={20}
{...getIconProps('success')}
/>
</TouchableOpacity>
</View>
) : (
@ -93,7 +98,10 @@ export default function EditableText({
</Text>
<View className="mt-1">
<View className="flex-row items-center self-start px-1.5 py-1 rounded bg-muted/20">
<Edit2 size={14} className="text-muted-foreground" />
<Edit2
size={14}
{...getIconProps('muted')}
/>
<Text className="text-xs text-muted-foreground ml-1">Edit</Text>
</View>
</View>

View File

@ -10,19 +10,42 @@ import UserAvatar from '@/components/UserAvatar';
import PowerLogo from '@/components/PowerLogo';
import { useSettingsDrawer } from '@/lib/contexts/SettingsDrawerContext';
import { useNDKCurrentUser } from '@/lib/hooks/useNDK';
import { useIconColor } from '@/lib/theme/iconUtils';
interface HeaderProps {
title?: string;
hideTitle?: boolean;
rightElement?: React.ReactNode;
useLogo?: boolean;
showNotifications?: boolean; // New prop
}
function NotificationBell() {
const { getIconProps } = useIconColor();
return (
<Button
variant="ghost"
size="icon"
onPress={() => console.log('Open notifications')}
>
<View className="relative">
<Bell
size={24}
{...getIconProps('primary')}
/>
<View className="absolute -top-1 -right-1 w-2 h-2 bg-primary rounded-full" />
</View>
</Button>
);
}
export default function Header({
title,
hideTitle = false,
rightElement,
useLogo = false
useLogo = false,
showNotifications = true // Default to true
}: HeaderProps) {
const theme = useTheme();
const insets = useSafeAreaInsets();
@ -39,6 +62,13 @@ export default function Header({
if (hideTitle) return null;
// Determine right element: custom, notification bell, or nothing
const headerRightElement = rightElement
? rightElement
: showNotifications
? <NotificationBell />
: null;
return (
<View
style={[
@ -68,21 +98,9 @@ export default function Header({
)}
</View>
{/* Right side - Custom element or default notifications */}
{/* Right side - Custom element, notifications, or nothing */}
<View style={styles.rightContainer}>
{rightElement || (
<Button
variant="ghost"
size="icon"
onPress={() => {}}
>
<View className="relative">
<Bell size={24} color={theme.colors.text} />
{/* Notification indicator - you can conditionally render this */}
<View className="absolute -top-1 -right-1 w-2 h-2 bg-primary rounded-full" />
</View>
</Button>
)}
{headerRightElement}
</View>
</View>
</View>

View File

@ -14,7 +14,7 @@ import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Separator } from '@/components/ui/separator';
import { Text } from '@/components/ui/text';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import NostrLoginSheet from '@/components/sheets/NostrLoginSheet';
import RelayManagement from '@/components/RelayManagement';
import { useNDKCurrentUser, useNDKAuth } from '@/lib/hooks/useNDK';

View File

@ -2,7 +2,7 @@ import { Pressable, View } from 'react-native';
import { setAndroidNavigationBar } from '@/lib/android-navigation-bar';
import { MoonStar } from '@/lib/icons/MoonStar';
import { Sun } from '@/lib/icons/Sun';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import { cn } from '@/lib/utils';
export function ThemeToggle() {

View File

@ -19,7 +19,7 @@ import {
} from 'lucide-react-native';
import { ExerciseDisplay } from '@/types/exercise';
import { useTheme } from '@react-navigation/native';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import type { CustomTheme } from '@/lib/theme';
const Tab = createMaterialTopTabNavigator();

View File

@ -7,7 +7,7 @@ import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { generateId } from '@/utils/ids';
import { X } from 'lucide-react-native';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import {
BaseExercise,
ExerciseType,

View File

@ -17,7 +17,7 @@ import { generateId } from '@/utils/ids';
import { useSQLiteContext } from 'expo-sqlite';
import { LibraryService } from '@/lib/db/services/LibraryService';
import { ChevronLeft, Dumbbell, Clock, RotateCw, List, Search, X } from 'lucide-react-native';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
interface NewTemplateSheetProps {
isOpen: boolean;

View File

@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { X, Info } from 'lucide-react-native';
import { useNDKAuth } from '@/lib/hooks/useNDK';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
interface NostrLoginSheetProps {
open: boolean;

View File

@ -9,7 +9,7 @@ import { Card, CardContent } from '@/components/ui/card';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import { NavigationContainer } from '@react-navigation/native';
import { useTheme } from '@react-navigation/native';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import { WorkoutTemplate } from '@/types/templates';
import { useWorkoutStore } from '@/stores/workoutStore';
import { formatTime } from '@/utils/formatTime';

View File

@ -18,6 +18,7 @@ import {
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { Template, TemplateExerciseDisplay } from '@/types/templates';
import { useIconColor } from '@/lib/theme/iconUtils';
interface TemplateCardProps {
template: Template;
@ -36,6 +37,7 @@ export function TemplateCard({
}: TemplateCardProps) {
const [showDeleteAlert, setShowDeleteAlert] = React.useState(false);
const lastUsed = template.metadata?.lastUsed ? new Date(template.metadata.lastUsed) : undefined;
const { getIconProps, getIconColor } = useIconColor();
const {
id,
@ -147,7 +149,10 @@ export function TemplateCard({
className="native:h-10 native:w-10"
accessibilityLabel="Start workout"
>
<Play className="text-primary" size={20} />
<Play
size={20}
{...getIconProps('primary')}
/>
</Button>
<Button
variant="ghost"
@ -157,9 +162,9 @@ export function TemplateCard({
accessibilityLabel={isFavorite ? "Remove from favorites" : "Add to favorites"}
>
<Star
className={isFavorite ? "text-primary" : "text-muted-foreground"}
fill={isFavorite ? "currentColor" : "none"}
size={20}
size={20}
{...getIconProps(isFavorite ? 'primary' : 'muted')}
fill={isFavorite ? getIconColor('primary') : "none"}
/>
</Button>
<AlertDialog open={showDeleteAlert} onOpenChange={setShowDeleteAlert}>
@ -171,12 +176,8 @@ export function TemplateCard({
accessibilityLabel="Delete template"
>
<Trash2
size={20}
color={Platform.select({
ios: undefined,
android: '#8B5CF6'
})}
className="text-destructive"
size={20}
{...getIconProps('destructive')}
/>
</Button>
</AlertDialogTrigger>

View File

@ -2,8 +2,8 @@
import React from 'react';
import { TouchableOpacity, View, StyleSheet } from 'react-native';
import { X } from 'lucide-react-native';
import { useColorScheme } from '@/lib/useColorScheme';
import { NAV_THEME } from '@/lib/constants';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import { NAV_THEME } from '@/lib/theme/constants';
interface CloseButtonProps {
onPress: () => void;

View File

@ -7,7 +7,7 @@ import Animated, {
useDerivedValue,
withTiming,
} from 'react-native-reanimated';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import { cn } from '@/lib/utils';
const SwitchWeb = React.forwardRef<SwitchPrimitives.RootRef, SwitchPrimitives.RootProps>(

View File

@ -5,7 +5,7 @@ import { Text } from '@/components/ui/text';
import { Circle, CheckCircle } from 'lucide-react-native'; // Lucide React icons
import { cn } from '@/lib/utils';
import { useWorkoutStore } from '@/stores/workoutStore';
import { useColorScheme } from '@/lib/useColorScheme';
import { useColorScheme } from '@/lib/theme/useColorScheme';
import type { WorkoutSet } from '@/types/workout';
import debounce from 'lodash/debounce';

View File

@ -1,6 +1,6 @@
import * as NavigationBar from 'expo-navigation-bar';
import { Platform } from 'react-native';
import { NAV_THEME } from '@/lib/constants';
import { NAV_THEME } from '@/lib/theme/constants';
export async function setAndroidNavigationBar(theme: 'light' | 'dark') {
if (Platform.OS !== 'android') return;

View File

@ -1,30 +0,0 @@
// lib/constants.ts
import type { NavigationThemeColors } from './theme';
export const NAV_THEME: {
light: NavigationThemeColors;
dark: NavigationThemeColors;
} = {
light: {
background: 'hsl(0, 0%, 100%)',
border: 'hsl(240, 5.9%, 90%)',
card: 'hsl(0, 0%, 100%)',
notification: 'hsl(0, 84.2%, 60.2%)',
primary: 'hsl(261, 90%, 66%)',
text: 'hsl(240, 10%, 3.9%)',
tabActive: '#000000',
tabInactive: '#737373',
tabIndicator: 'hsl(261, 90%, 66%)',
},
dark: {
background: 'hsl(240, 10%, 3.9%)',
border: 'hsl(240, 3.7%, 15.9%)',
card: 'hsl(240, 10%, 3.9%)',
notification: 'hsl(0, 72%, 51%)',
primary: 'hsl(261, 90%, 66%)',
text: 'hsl(0, 0%, 98%)',
tabActive: '#FFFFFF',
tabInactive: '#A3A3A3',
tabIndicator: 'hsl(261, 90%, 66%)',
},
};

63
lib/theme/colors.ts Normal file
View File

@ -0,0 +1,63 @@
// lib/theme/colors.ts
export const COLORS = {
// Primary brand colors
purple: {
DEFAULT: 'hsl(261, 90%, 66%)',
pressed: 'hsl(262, 84%, 58%)',
light: 'hsl(261, 90%, 85%)',
dark: 'hsl(261, 90%, 45%)',
},
// Semantic colors
success: 'hsl(142, 71%, 45%)',
warning: 'hsl(38, 92%, 50%)',
destructive: 'hsl(0, 84.2%, 60.2%)',
// Light mode
light: {
background: 'hsl(0, 0%, 100%)',
foreground: 'hsl(240, 10%, 3.9%)',
card: 'hsl(0, 0%, 100%)',
cardForeground: 'hsl(240, 10%, 3.9%)',
border: 'hsl(240, 5.9%, 90%)',
input: 'hsl(240, 5.9%, 90%)',
muted: 'hsl(240, 4.8%, 95.9%)',
mutedForeground: 'hsl(240, 3.8%, 46.1%)',
},
// Dark mode
dark: {
background: 'hsl(240, 10%, 3.9%)',
foreground: 'hsl(0, 0%, 98%)',
card: 'hsl(240, 10%, 5.9%)',
cardForeground: 'hsl(0, 0%, 98%)',
border: 'hsl(240, 3.7%, 25%)',
input: 'hsl(240, 3.7%, 25%)',
muted: 'hsl(240, 3.7%, 18%)',
mutedForeground: 'hsl(240, 5%, 64.9%)',
},
};
// Fixed color values to use when className doesn't work (especially for icons on Android)
export const FIXED_COLORS = {
// Primary colors
primary: COLORS.purple.DEFAULT,
primaryDark: COLORS.purple.dark,
// Text colors
text: {
light: COLORS.light.foreground,
dark: COLORS.dark.foreground,
},
// Semantic colors
destructive: 'hsl(0, 84.2%, 60.2%)',
success: 'hsl(142, 71%, 45%)',
warning: 'hsl(38, 92%, 50%)',
// Muted colors
muted: {
light: COLORS.light.mutedForeground,
dark: COLORS.dark.mutedForeground,
}
};

42
lib/theme/constants.ts Normal file
View File

@ -0,0 +1,42 @@
// lib/theme/constants.ts
import { COLORS } from './colors';
export interface NavigationThemeColors {
background: string;
border: string;
card: string;
notification: string;
primary: string;
text: string;
tabActive: string;
tabInactive: string;
tabIndicator: string;
}
export const NAV_THEME: {
light: NavigationThemeColors;
dark: NavigationThemeColors;
} = {
light: {
background: COLORS.light.background,
border: COLORS.light.border,
card: COLORS.light.card,
notification: COLORS.destructive,
primary: COLORS.purple.DEFAULT,
text: COLORS.light.foreground,
tabActive: COLORS.light.foreground,
tabInactive: COLORS.light.mutedForeground,
tabIndicator: COLORS.purple.DEFAULT,
},
dark: {
background: COLORS.dark.background,
border: COLORS.dark.border,
card: COLORS.dark.card,
notification: COLORS.destructive,
primary: COLORS.purple.DEFAULT,
text: COLORS.dark.foreground,
tabActive: COLORS.dark.foreground,
tabInactive: COLORS.dark.mutedForeground,
tabIndicator: COLORS.purple.DEFAULT,
},
};

41
lib/theme/iconUtils.ts Normal file
View File

@ -0,0 +1,41 @@
// lib/theme/iconUtils.ts
import { Platform } from 'react-native';
import { useColorScheme } from './useColorScheme';
import { FIXED_COLORS } from './colors';
export type IconVariant =
| 'primary'
| 'muted'
| 'destructive'
| 'success'
| 'warning';
export function useIconColor() {
const { isDarkColorScheme } = useColorScheme();
const getIconColor = (variant: IconVariant = 'primary') => {
// Use fixed colors that work on both platforms
switch (variant) {
case 'primary':
return FIXED_COLORS.primary;
case 'muted':
return isDarkColorScheme ? FIXED_COLORS.muted.dark : FIXED_COLORS.muted.light;
case 'destructive':
return FIXED_COLORS.destructive;
case 'success':
return FIXED_COLORS.success;
case 'warning':
return FIXED_COLORS.warning;
default:
return FIXED_COLORS.primary;
}
};
return {
getIconColor,
getIconProps: (variant: IconVariant = 'primary') => ({
color: getIconColor(variant),
strokeWidth: Platform.OS === 'android' ? 2 : 1.5,
})
};
}

9
lib/theme/index.ts Normal file
View File

@ -0,0 +1,9 @@
// lib/theme/index.ts
export * from './colors';
export * from './constants';
export * from './iconUtils';
export * from './useColorScheme';
// Also re-export any types
export type { NavigationThemeColors } from '@/lib/theme/constants';
export type { IconVariant } from './iconUtils';

View File

@ -1,3 +1,4 @@
// lib/theme/useColorScheme.ts
import { useColorScheme as useNativewindColorScheme } from 'nativewind';
export function useColorScheme() {
@ -8,4 +9,4 @@ export function useColorScheme() {
setColorScheme,
toggleColorScheme,
};
}
}

View File

@ -604,42 +604,42 @@ const useWorkoutStoreBase = create<ExtendedWorkoutState & ExtendedWorkoutActions
},
// Template Management
startWorkoutFromTemplate: async (templateId: string) => {
// Get template from your template store/service
const template = await getTemplate(templateId);
startWorkoutFromTemplate: async (templateId: string, templateData?: WorkoutTemplate) => {
// If template data is provided directly, use it
const template = templateData || await getTemplate(templateId);
if (!template) return;
// Convert template exercises to workout exercises
const exercises: WorkoutExercise[] = template.exercises.map(templateExercise => ({
id: generateId('local'),
title: templateExercise.exercise.title,
type: templateExercise.exercise.type,
category: templateExercise.exercise.category,
equipment: templateExercise.exercise.equipment,
tags: templateExercise.exercise.tags || [],
availability: {
source: ['local']
},
created_at: Date.now(),
sets: Array(templateExercise.targetSets || 3).fill(0).map(() => ({
id: generateId('local'),
title: templateExercise.exercise.title,
type: templateExercise.exercise.type,
category: templateExercise.exercise.category,
equipment: templateExercise.exercise.equipment,
tags: templateExercise.exercise.tags || [],
availability: {
source: ['local']
},
created_at: Date.now(),
sets: Array(templateExercise.targetSets || 3).fill(0).map(() => ({
id: generateId('local'),
type: 'normal',
weight: 0,
reps: templateExercise.targetReps || 0,
isCompleted: false
})),
isCompleted: false,
notes: templateExercise.notes || ''
}));
// Start workout with template data
get().startWorkout({
title: template.title,
type: template.type || 'strength',
exercises,
templateId: template.id
});
},
type: 'normal',
weight: 0,
reps: templateExercise.targetReps || 0,
isCompleted: false
})),
isCompleted: false,
notes: templateExercise.notes || ''
}));
// Start workout with template data
get().startWorkout({
title: template.title,
type: template.type || 'strength',
exercises,
templateId: template.id
});
},
updateWorkoutTitle: (title: string) => {
const { activeWorkout } = get();