mirror of
https://github.com/DocNR/POWR.git
synced 2025-06-03 15:52:06 +00:00
android icon and other bug fixes
This commit is contained in:
parent
43d6d7d12b
commit
43df1aeb79
@ -14,7 +14,7 @@ export default function HistoryLayout() {
|
||||
|
||||
return (
|
||||
<TabScreen>
|
||||
<Header title="History" useLogo={true} />
|
||||
<Header useLogo={true} showNotifications={true} />
|
||||
|
||||
<Tab.Navigator
|
||||
initialRouteName="history"
|
||||
|
@ -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"
|
||||
|
@ -16,7 +16,7 @@ export default function LibraryLayout() {
|
||||
|
||||
return (
|
||||
<TabScreen>
|
||||
<Header useLogo={true} />
|
||||
<Header useLogo={true} showNotifications={true} />
|
||||
|
||||
<Tab.Navigator
|
||||
initialRouteName="templates"
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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"
|
||||
|
@ -16,7 +16,7 @@ export default function SocialLayout() {
|
||||
|
||||
return (
|
||||
<TabScreen>
|
||||
<Header useLogo={true} />
|
||||
<Header useLogo={true} showNotifications={true} />
|
||||
|
||||
<Tab.Navigator
|
||||
initialRouteName="powr"
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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';
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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';
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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>(
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
63
lib/theme/colors.ts
Normal 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
42
lib/theme/constants.ts
Normal 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
41
lib/theme/iconUtils.ts
Normal 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
9
lib/theme/index.ts
Normal 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';
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user