small UI update to library tab

This commit is contained in:
DocNR 2025-02-12 12:55:51 -05:00
parent 656e79ba91
commit 8c7db4e778
18 changed files with 540 additions and 371 deletions

BIN
app/(tabs)/._layout.tsx.swp Normal file

Binary file not shown.

View File

@ -2,26 +2,27 @@
import React from 'react';
import { Platform } from 'react-native';
import { Tabs } from 'expo-router';
import { useTheme } from '@react-navigation/native'; // Change this import
import { useTheme } from '@react-navigation/native';
import { Dumbbell, Library, Users, History, User } from 'lucide-react-native';
import { CUSTOM_COLORS } from '@/lib/constants';
import { convertHSLValues } from '@/lib/theme';
export default function TabLayout() {
const { colors } = useTheme();
const { colors, dark } = useTheme();
const { purple, mutedForeground } = convertHSLValues(dark ? 'dark' : 'light');
return (
<Tabs
screenOptions={{
headerShown: false,
tabBarStyle: {
backgroundColor: colors.card,
backgroundColor: colors.background,
borderTopColor: colors.border,
borderTopWidth: Platform.OS === 'ios' ? 0.5 : 1,
elevation: 0,
shadowOpacity: 0,
},
tabBarActiveTintColor: CUSTOM_COLORS.purple,
tabBarInactiveTintColor: colors.text, // Changed this from colors.background
tabBarActiveTintColor: purple,
tabBarInactiveTintColor: mutedForeground,
tabBarShowLabel: true,
tabBarLabelStyle: {
fontSize: 12,

View File

@ -1,41 +1,106 @@
// app/(tabs)/library/_layout.native.tsx
import { View } from 'react-native';
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import { useTheme } from '@react-navigation/native';
import { Text } from '@/components/ui/text';
import { ThemeToggle } from '@/components/ThemeToggle';
import { SearchPopover } from '@/components/library/SearchPopover';
import { FilterPopover } from '@/components/library/FilterPopover';
import { FilterSheet, type FilterOptions, type SourceType } from '@/components/library/FilterSheet';
import ExercisesScreen from './exercises';
import TemplatesScreen from './templates';
import ProgramsScreen from './programs';
import { CUSTOM_COLORS } from '@/lib/constants';
import Header from '@/components/Header';
import { useState } from 'react';
import { useTheme } from '@react-navigation/native';
import { convertHSLValues } from '@/lib/theme';
const Tab = createMaterialTopTabNavigator();
// Default available filters
const availableFilters = {
equipment: ['Barbell', 'Dumbbell', 'Bodyweight', 'Machine', 'Cables', 'Other'],
tags: ['Strength', 'Cardio', 'Mobility', 'Recovery'],
source: ['local', 'powr', 'nostr'] as SourceType[] // Fixed: Create mutable array of SourceType
};
// Initial filter state
const initialFilters: FilterOptions = {
equipment: [],
tags: [],
source: []
};
export default function LibraryLayout() {
const { colors } = useTheme();
const { colors, dark } = useTheme();
const { purple, mutedForeground } = convertHSLValues(dark ? 'dark' : 'light');
const [searchQuery, setSearchQuery] = useState('');
const [activeFilters, setActiveFilters] = useState(0);
const [filterSheetOpen, setFilterSheetOpen] = useState(false);
const [currentFilters, setCurrentFilters] = useState<FilterOptions>(initialFilters);
const handleApplyFilters = (filters: FilterOptions) => {
setCurrentFilters(filters);
// Count total active filters
const totalFilters = Object.values(filters).reduce(
(acc, curr) => acc + curr.length,
0
);
setActiveFilters(totalFilters);
};
return (
<View className="flex-1">
{/* Header */}
<View className="flex-row justify-between items-center px-4 pt-14 pb-4 bg-card">
<Text className="text-2xl font-bold">Library</Text>
<ThemeToggle />
</View>
<Header
title="Library"
rightElement={
<View className="flex-row items-center gap-2">
<SearchPopover
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
/>
<FilterPopover
activeFilters={activeFilters}
onOpenFilters={() => setFilterSheetOpen(true)}
/>
<ThemeToggle />
</View>
}
>
<Text className="text-muted-foreground text-sm mt-1">
12 exercises 5 templates
</Text>
</Header>
<FilterSheet
isOpen={filterSheetOpen}
onClose={() => setFilterSheetOpen(false)}
options={currentFilters}
onApplyFilters={handleApplyFilters}
availableFilters={availableFilters}
/>
<Tab.Navigator
initialRouteName="templates"
screenOptions={{
tabBarActiveTintColor: CUSTOM_COLORS.purple,
tabBarInactiveTintColor: colors.text,
tabBarActiveTintColor: purple,
tabBarInactiveTintColor: mutedForeground,
tabBarLabelStyle: {
fontSize: 14,
textTransform: 'capitalize',
fontWeight: 'bold',
},
tabBarIndicatorStyle: {
backgroundColor: CUSTOM_COLORS.purple,
backgroundColor: purple,
height: 2,
},
tabBarStyle: { backgroundColor: colors.card }
tabBarStyle: {
backgroundColor: colors.background,
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
tabBarPressColor: colors.primary,
}}
>
<Tab.Screen

View File

@ -1,106 +1,59 @@
// app/(tabs)/library/exercises.tsx
import React, { useState, useCallback } from 'react';
import React, { useState } from 'react';
import { View, ScrollView } from 'react-native';
import { Text } from '@/components/ui/text';
import { ExerciseCard } from '@/components/exercises/ExerciseCard';
import { FloatingActionButton } from '@/components/shared/FloatingActionButton';
import { SearchHeader } from '@/components/library/SearchHeader';
import { FilterSheet, type FilterOptions } from '@/components/library/FilterSheet';
import { NewExerciseSheet } from '@/components/library/NewExerciseSheet';
import { Dumbbell } from 'lucide-react-native';
import { Exercise, ExerciseCategory, ExerciseEquipment, ContentSource } from '@/types/library';
import { Exercise } from '@/types/library';
const initialExercises: Exercise[] = [
{
id: '1',
title: 'Barbell Back Squat',
category: 'Legs' as ExerciseCategory,
equipment: 'barbell' as ExerciseEquipment,
category: 'Legs',
equipment: 'barbell',
tags: ['compound', 'strength'],
source: 'local' as ContentSource,
source: 'local',
description: 'A compound exercise that primarily targets the quadriceps, hamstrings, and glutes.',
},
{
id: '2',
title: 'Pull-ups',
category: 'Pull' as ExerciseCategory,
equipment: 'bodyweight' as ExerciseEquipment,
category: 'Pull',
equipment: 'bodyweight',
tags: ['upper-body', 'compound'],
source: 'local' as ContentSource,
source: 'local',
description: 'An upper body pulling exercise that targets the latissimus dorsi and biceps.',
},
{
id: '3',
title: 'Bench Press',
category: 'Push' as ExerciseCategory,
equipment: 'barbell' as ExerciseEquipment,
category: 'Push',
equipment: 'barbell',
tags: ['push', 'strength'],
source: 'nostr' as ContentSource,
source: 'nostr',
description: 'A compound pushing exercise that targets the chest, shoulders, and triceps.',
},
];
export default function ExercisesScreen() {
const [exercises, setExercises] = useState<Exercise[]>(initialExercises);
const [searchQuery, setSearchQuery] = useState('');
const [showFilters, setShowFilters] = useState(false);
const [showNewExercise, setShowNewExercise] = useState(false);
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
equipment: [],
tags: [],
source: []
});
// Filter exercises
const filteredExercises = useCallback(() => {
return exercises.filter(exercise => {
// Search filter - make case insensitive
if (searchQuery.trim()) {
const searchLower = searchQuery.toLowerCase().trim();
const matchesSearch =
exercise.title.toLowerCase().includes(searchLower) ||
exercise.description?.toLowerCase().includes(searchLower) ||
exercise.tags.some(tag => tag.toLowerCase().includes(searchLower)) ||
exercise.equipment?.toLowerCase().includes(searchLower);
if (!matchesSearch) return false;
}
// Equipment filter
if (filterOptions.equipment.length > 0) {
if (!filterOptions.equipment.includes(exercise.equipment || '')) {
return false;
}
}
// Tags filter
if (filterOptions.tags.length > 0) {
if (!exercise.tags.some(tag => filterOptions.tags.includes(tag))) {
return false;
}
}
// Source filter
if (filterOptions.source.length > 0) {
if (!filterOptions.source.includes(exercise.source)) {
return false;
}
}
return true;
});
}, [exercises, searchQuery, filterOptions]);
const handleAddExercise = (newExercise: Exercise) => {
const handleAddExercise = (exerciseData: Omit<Exercise, 'id' | 'source'>) => {
const newExercise: Exercise = {
...exerciseData,
id: crypto.randomUUID(),
source: 'local',
};
setExercises(prev => [...prev, newExercise]);
setShowNewExercise(false);
};
// Get recent and filtered exercises
// Get recent exercises
const recentExercises = exercises.slice(0, 2);
const allExercises = filteredExercises();
const activeFilterCount = Object.values(filterOptions)
.reduce((count, filters) => count + filters.length, 0);
const handleDelete = (id: string) => {
setExercises(current => current.filter(ex => ex.id !== id));
@ -110,21 +63,8 @@ export default function ExercisesScreen() {
console.log('Selected exercise:', exerciseId);
};
const availableFilters = {
equipment: ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'other'] as ExerciseEquipment[],
tags: ['strength', 'compound', 'isolation', 'push', 'pull', 'legs'],
source: ['local', 'powr', 'nostr'] as ContentSource[]
};
return (
<View className="flex-1 bg-background">
<SearchHeader
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
activeFilters={activeFilterCount}
onOpenFilters={() => setShowFilters(true)}
/>
<ScrollView className="flex-1">
{/* Recent Exercises Section */}
<View className="py-4">
@ -145,7 +85,7 @@ export default function ExercisesScreen() {
<View className="py-4">
<Text className="text-lg font-semibold mb-4 px-4">All Exercises</Text>
<View className="gap-3">
{allExercises.map(exercise => (
{exercises.map(exercise => (
<ExerciseCard
key={exercise.id}
{...exercise}
@ -162,14 +102,6 @@ export default function ExercisesScreen() {
onPress={() => setShowNewExercise(true)}
/>
<FilterSheet
isOpen={showFilters}
onClose={() => setShowFilters(false)}
options={filterOptions}
onApplyFilters={setFilterOptions}
availableFilters={availableFilters}
/>
<NewExerciseSheet
isOpen={showNewExercise}
onClose={() => setShowNewExercise(false)}

View File

@ -1,15 +1,12 @@
// app/(tabs)/library/templates.tsx
import { View, ScrollView } from 'react-native';
import { useState, useCallback } from 'react';
import { useState } from 'react';
import { Text } from '@/components/ui/text';
import { TemplateCard } from '@/components/templates/TemplateCard';
import { FloatingActionButton } from '@/components/shared/FloatingActionButton';
import { SearchHeader } from '@/components/library/SearchHeader';
import { FilterSheet } from '@/components/library/FilterSheet';
import { NewTemplateSheet } from '@/components/library/NewTemplateSheet';
import { useRouter } from 'expo-router';
import { Plus } from 'lucide-react-native';
import { Template, FilterOptions, ContentSource, ExerciseEquipment } from '@/types/library';
import { Template } from '@/types/library';
// Mock data - move to a separate file later
const initialTemplates: Template[] = [
@ -44,58 +41,9 @@ const initialTemplates: Template[] = [
];
export default function TemplatesScreen() {
const [searchQuery, setSearchQuery] = useState('');
const [showFilters, setShowFilters] = useState(false);
const [showNewTemplate, setShowNewTemplate] = useState(false);
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
equipment: [],
tags: [],
source: []
});
const [templates, setTemplates] = useState(initialTemplates);
const availableFilters = {
equipment: ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'other'] as ExerciseEquipment[],
tags: ['strength', 'circuit', 'emom', 'amrap', 'Full Body', 'Upper Body', 'Lower Body', 'Conditioning'],
source: ['local', 'powr', 'nostr'] as ContentSource[]
};
const filteredTemplates = useCallback(() => {
return templates.filter(template => {
// Search filter
if (searchQuery) {
const searchLower = searchQuery.toLowerCase();
if (!template.title.toLowerCase().includes(searchLower) &&
!template.exercises.some(ex =>
ex.title.toLowerCase().includes(searchLower)
)) {
return false;
}
}
// Tags filter (includes type and category)
if (filterOptions.tags.length > 0) {
if (!filterOptions.tags.includes(template.type) &&
!filterOptions.tags.includes(template.category) &&
!template.tags.some(tag => filterOptions.tags.includes(tag))) {
return false;
}
}
// Source filter
if (filterOptions.source.length > 0) {
if (!filterOptions.source.includes(template.source)) {
return false;
}
}
return true;
});
}, [templates, searchQuery, filterOptions]);
const activeFilterCount = Object.values(filterOptions)
.reduce((count, filters) => count + filters.length, 0);
const handleDelete = (id: string) => {
setTemplates(current => current.filter(t => t.id !== id));
};
@ -131,13 +79,6 @@ export default function TemplatesScreen() {
return (
<View className="flex-1 bg-background">
<SearchHeader
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
activeFilters={activeFilterCount}
onOpenFilters={() => setShowFilters(true)}
/>
<ScrollView className="flex-1">
{/* Favorites Section */}
{favoriteTemplates.length > 0 && (
@ -201,14 +142,6 @@ export default function TemplatesScreen() {
onClose={() => setShowNewTemplate(false)}
onSubmit={handleAddTemplate}
/>
<FilterSheet
isOpen={showFilters}
onClose={() => setShowFilters(false)}
options={filterOptions}
onApplyFilters={setFilterOptions}
availableFilters={availableFilters}
/>
</View>
);
}

View File

@ -7,10 +7,13 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import Header from '@/components/Header';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const PLACEHOLDER_IMAGE = 'https://github.com/shadcn.png'; // Placeholder profile image
const PLACEHOLDER_IMAGE = 'https://github.com/shadcn.png';
export default function ProfileScreen() {
const insets = useSafeAreaInsets();
return (
<View className="flex-1">
<Header
@ -20,7 +23,6 @@ export default function ProfileScreen() {
variant="ghost"
size="icon"
onPress={() => {
// TODO: Navigate to settings
console.log('Open settings');
}}
>
@ -29,7 +31,12 @@ export default function ProfileScreen() {
}
/>
<ScrollView className="flex-1">
<ScrollView
className="flex-1"
contentContainerStyle={{
paddingBottom: insets.bottom + 20
}}
>
{/* Profile Header Section */}
<View className="items-center pt-6 pb-8">
<Avatar className="w-24 h-24 mb-4" alt="Profile picture">
@ -64,7 +71,6 @@ export default function ProfileScreen() {
variant="outline"
className="mb-2"
onPress={() => {
// TODO: Navigate to edit profile
console.log('Edit profile');
}}
>
@ -75,7 +81,6 @@ export default function ProfileScreen() {
variant="outline"
className="mb-2"
onPress={() => {
// TODO: Navigate to account settings
console.log('Account settings');
}}
>
@ -86,14 +91,12 @@ export default function ProfileScreen() {
variant="outline"
className="mb-2"
onPress={() => {
// TODO: Navigate to preferences
console.log('Preferences');
}}
>
<Text>Preferences</Text>
</Button>
</View>
</ScrollView>
</View>
);

View File

@ -1,11 +1,32 @@
// app/(tabs)/index.tsx (and similar for other tab screens)
import { View } from 'react-native';
import { Text } from '@/components/ui/text';
import { Button } from '@/components/ui/button';
import { Bell } from 'lucide-react-native';
import Header from '@/components/Header';
export default function HomeScreen() {
export default function SocialScreen() {
return (
<View className="flex-1 items-center justify-center">
<Text>Home Screen</Text>
<View className="flex-1">
<Header
title="Social"
rightElement={
<Button
variant="ghost"
size="icon"
onPress={() => {
// TODO: Open notifications
console.log('Open notifications');
}}
>
{/* Add a notification badge if needed */}
<View className="relative">
<Bell className="text-foreground" />
<View className="absolute -top-1 -right-1 w-2 h-2 bg-primary rounded-full" />
</View>
</Button>
}
/>
</View>
);
}

View File

@ -1,19 +1,36 @@
// components/Header.tsx
import React from 'react';
import { View } from 'react-native';
import { View, Platform } from 'react-native';
import { Text } from '@/components/ui/text';
import { ThemeToggle } from '@/components/ThemeToggle';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
interface HeaderProps {
title: string;
rightElement?: React.ReactNode;
children?: React.ReactNode;
}
export default function Header({ title, rightElement }: HeaderProps) {
export default function Header({ title, rightElement, children }: HeaderProps) {
const insets = useSafeAreaInsets();
return (
<View className="flex-row justify-between items-center px-4 pt-14 pb-4 bg-card">
<Text className="text-2xl font-bold">{title}</Text>
{rightElement || <ThemeToggle />}
<View
className="flex-row items-center justify-between bg-card border-b border-border"
style={{
paddingTop: Platform.OS === 'ios' ? insets.top : insets.top + 8,
paddingHorizontal: 16,
paddingBottom: 12,
}}
>
<View className="flex-1">
<Text className="text-2xl font-bold">{title}</Text>
{children}
</View>
{rightElement && (
<View className="ml-4">
{rightElement}
</View>
)}
</View>
);
}

View File

@ -0,0 +1,37 @@
// components/library/FilterPopover.tsx
import React from 'react';
import { View } from 'react-native';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Button } from '@/components/ui/button';
import { Filter } from 'lucide-react-native';
import { Badge } from '@/components/ui/badge';
import { FilterOptions } from './FilterSheet';
import { Text } from 'components/ui/text';
interface FilterPopoverProps {
activeFilters: number;
onOpenFilters: () => void;
}
export function FilterPopover({ activeFilters, onOpenFilters }: FilterPopoverProps) {
return (
<View className="relative">
<Button
variant="ghost"
size="icon"
onPress={onOpenFilters}
>
<Filter className="text-foreground" />
</Button>
{activeFilters > 0 && (
<Badge
className="absolute -top-2 -right-2 w-5 h-5 flex items-center justify-center p-0"
>
<Text className="text-xs text-primary-foreground">
{activeFilters}
</Text>
</Badge>
)}
</View>
);
}

View File

@ -74,6 +74,13 @@ export function FilterSheet({
}: FilterSheetProps) {
const [localOptions, setLocalOptions] = React.useState(options);
const handleReset = () => {
const resetOptions = { equipment: [], tags: [], source: [] };
setLocalOptions(resetOptions);
// Immediately apply reset
onApplyFilters(resetOptions);
};
const toggleFilter = (
category: keyof FilterOptions,
value: string | SourceType
@ -88,9 +95,6 @@ export function FilterSheet({
return (
<Sheet isOpen={isOpen} onClose={onClose}>
<SheetHeader>
<SheetTitle>Filter Exercises</SheetTitle>
</SheetHeader>
<SheetContent>
<View className="gap-4">
<Accordion type="single" collapsible>
@ -102,7 +106,7 @@ export function FilterSheet({
<Button
variant="outline"
className="flex-1"
onPress={() => setLocalOptions({ equipment: [], tags: [], source: [] })}
onPress={handleReset}
>
<Text>Reset</Text>
</Button>

View File

@ -0,0 +1,47 @@
// components/library/SearchPopover.tsx
import React from 'react';
import { View } from 'react-native';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Search, X } from 'lucide-react-native';
interface SearchPopoverProps {
searchQuery: string;
onSearchChange: (query: string) => void;
}
export function SearchPopover({ searchQuery, onSearchChange }: SearchPopoverProps) {
const [isOpen, setIsOpen] = React.useState(false);
return (
<Popover onOpenChange={setIsOpen}>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon">
<Search className="text-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-80 p-0" align="end">
<View className="flex-row items-center p-2 gap-2">
<Input
placeholder="Search exercises..."
value={searchQuery}
onChangeText={onSearchChange}
className="flex-1"
autoFocus
placeholderTextColor="text-muted-foreground"
/>
{searchQuery.length > 0 && (
<Button
variant="ghost"
size="icon"
onPress={() => onSearchChange('')}
>
<X size={20} className="text-muted-foreground" />
</Button>
)}
</View>
</PopoverContent>
</Popover>
);
}

View File

@ -24,7 +24,7 @@ const buttonVariants = cva(
secondary: 'bg-secondary web:hover:opacity-80 active:opacity-80',
ghost: 'web:hover:bg-accent web:hover:text-accent-foreground active:bg-accent',
link: 'web:underline-offset-4 web:hover:underline web:focus:underline',
purple: 'bg-[#8B5CF6] web:hover:bg-[#7C3AED] active:bg-[#7C3AED]', // Added purple variant
purple: 'bg-[hsl(var(--purple))] text-white hover:bg-[hsl(var(--purple-pressed))]',
},
size: {
default: 'h-10 px-4 py-2 native:h-12 native:px-5 native:py-3',

View File

@ -24,6 +24,8 @@
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5.9% 10%;
--purple: 261 90% 66%;
--purple-pressed: 262 84% 58%;
}
.dark:root {

View File

@ -1,25 +1,30 @@
// lib/constants.ts
export const NAV_THEME = {
import type { NavigationThemeColors } from './theme';
export const NAV_THEME: {
light: NavigationThemeColors;
dark: NavigationThemeColors;
} = {
light: {
background: 'hsl(0 0% 100%)', // background
border: 'hsl(240 5.9% 90%)', // border
card: 'hsl(0 0% 100%)', // card
notification: 'hsl(0 84.2% 60.2%)', // destructive
primary: 'hsl(240 5.9% 10%)', // primary
text: 'hsl(240 10% 3.9%)', // foreground
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: 'hsl(261, 90%, 66%)',
tabInactive: 'hsl(240, 3.8%, 46.1%)',
tabIndicator: 'hsl(261, 90%, 66%)',
},
dark: {
background: 'hsl(240 10% 3.9%)', // background
border: 'hsl(240 3.7% 15.9%)', // border
card: 'hsl(240 10% 3.9%)', // card
notification: 'hsl(0 72% 51%)', // destructive
primary: 'hsl(0 0% 98%)', // primary
text: 'hsl(0 0% 98%)', // foreground
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: 'hsl(261, 90%, 66%)',
tabInactive: 'hsl(240, 5%, 64.9%)',
tabIndicator: 'hsl(261, 90%, 66%)',
},
};
export const CUSTOM_COLORS = {
purple: '#8B5CF6',
purplePressed: '#7C3AED', // Slightly darker for pressed state
orange: '#F97316'
} as const;

93
lib/theme.ts Normal file
View File

@ -0,0 +1,93 @@
// lib/theme.ts
import { Theme } from '@react-navigation/native';
import { ColorSchemeName } from 'react-native';
// Update the interface for our navigation theme colors
export interface NavigationThemeColors {
primary: string;
background: string;
card: string;
text: string;
border: string;
notification: string;
tabActive: string;
tabInactive: string;
tabIndicator: string;
}
// Original ThemeColors interface remains the same
export interface ThemeColors {
background: string;
foreground: string;
card: string;
'card-foreground': string;
popover: string;
'popover-foreground': string;
primary: string;
'primary-foreground': string;
secondary: string;
'secondary-foreground': string;
muted: string;
'muted-foreground': string;
accent: string;
'accent-foreground': string;
destructive: string;
'destructive-foreground': string;
border: string;
input: string;
ring: string;
purple: string;
'purple-pressed': string;
}
// Export the color conversion function
export function convertHSLValues(colorScheme: 'light' | 'dark') {
const purple = colorScheme === 'light'
? 'hsl(261, 90%, 66%)'
: 'hsl(261, 90%, 66%)';
const mutedForeground = colorScheme === 'light'
? 'hsl(240, 3.8%, 46.1%)'
: 'hsl(240, 5%, 64.9%)';
return {
purple,
mutedForeground,
};
}
export function getNavigationTheme(scheme: ColorSchemeName): Theme {
const colorScheme = scheme ?? 'light';
const { purple, mutedForeground } = convertHSLValues(colorScheme as 'light' | 'dark');
const theme: Theme = {
dark: colorScheme === 'dark',
colors: {
primary: purple,
background: colorScheme === 'dark' ? 'hsl(240, 10%, 3.9%)' : 'hsl(0, 0%, 100%)',
card: colorScheme === 'dark' ? 'hsl(240, 10%, 5.9%)' : 'hsl(0, 0%, 100%)',
text: colorScheme === 'dark' ? 'hsl(0, 0%, 98%)' : 'hsl(240, 10%, 3.9%)',
border: colorScheme === 'dark' ? 'hsl(240, 3.7%, 15.9%)' : 'hsl(240, 5.9%, 90%)',
notification: colorScheme === 'dark' ? 'hsl(0, 72%, 51%)' : 'hsl(0, 84.2%, 60.2%)',
},
fonts: {
regular: {
fontFamily: 'System',
fontWeight: '400',
},
medium: {
fontFamily: 'System',
fontWeight: '500',
},
bold: {
fontFamily: 'System',
fontWeight: '700',
},
heavy: {
fontFamily: 'System',
fontWeight: '900',
},
},
};
return theme;
}

299
package-lock.json generated
View File

@ -41,18 +41,18 @@
"@rn-primitives/types": "~1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"expo": "^52.0.25",
"expo": "~52.0.33",
"expo-linking": "~7.0.4",
"expo-navigation-bar": "~4.0.7",
"expo-navigation-bar": "~4.0.8",
"expo-router": "~4.0.16",
"expo-splash-screen": "~0.29.20",
"expo-status-bar": "~2.0.1",
"expo-system-ui": "~4.0.7",
"expo-system-ui": "~4.0.8",
"lucide-react-native": "^0.378.0",
"nativewind": "^4.1.23",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.6",
"react-native": "0.76.7",
"react-native-gesture-handler": "~2.20.2",
"react-native-pager-view": "^6.5.1",
"react-native-reanimated": "~3.16.1",
@ -166,13 +166,13 @@
}
},
"node_modules/@babel/generator": {
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
"version": "7.26.8",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz",
"integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.26.5",
"@babel/types": "^7.26.5",
"@babel/parser": "^7.26.8",
"@babel/types": "^7.26.8",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^3.0.2"
@ -515,12 +515,12 @@
}
},
"node_modules/@babel/parser": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
"version": "7.26.8",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz",
"integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.7"
"@babel/types": "^7.26.8"
},
"bin": {
"parser": "bin/babel-parser.js"
@ -2167,14 +2167,14 @@
}
},
"node_modules/@babel/template": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
"version": "7.26.8",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz",
"integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.25.9",
"@babel/parser": "^7.25.9",
"@babel/types": "^7.25.9"
"@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.26.8",
"@babel/types": "^7.26.8"
},
"engines": {
"node": ">=6.9.0"
@ -2200,16 +2200,16 @@
},
"node_modules/@babel/traverse--for-generate-function-map": {
"name": "@babel/traverse",
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
"version": "7.26.8",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz",
"integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.26.2",
"@babel/generator": "^7.26.5",
"@babel/parser": "^7.26.7",
"@babel/template": "^7.25.9",
"@babel/types": "^7.26.7",
"@babel/generator": "^7.26.8",
"@babel/parser": "^7.26.8",
"@babel/template": "^7.26.8",
"@babel/types": "^7.26.8",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@ -2218,9 +2218,9 @@
}
},
"node_modules/@babel/types": {
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
"version": "7.26.8",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz",
"integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.25.9",
@ -2255,16 +2255,16 @@
}
},
"node_modules/@expo/cli": {
"version": "0.22.12",
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.22.12.tgz",
"integrity": "sha512-i7mYb2s4RzlcLIsMewYtKol5rOIECOgLymAUfTwgvsAjg1r+11gmovsi3xdM0k1QiHmeJf0Wqz3sl7FJNsSKnw==",
"version": "0.22.15",
"resolved": "https://registry.npmjs.org/@expo/cli/-/cli-0.22.15.tgz",
"integrity": "sha512-iXG8E+Fcyq21yM0g5hwdRyAQ9G/T0B+fAXXhwNmUP1jyoqb5wZUmld3gYycsByEJbM2TX9xKMnjtq/zyjYxYOA==",
"license": "MIT",
"dependencies": {
"@0no-co/graphql.web": "^1.0.8",
"@babel/runtime": "^7.20.0",
"@expo/code-signing-certificates": "^0.0.5",
"@expo/config": "~10.0.8",
"@expo/config-plugins": "~9.0.14",
"@expo/config": "~10.0.9",
"@expo/config-plugins": "~9.0.15",
"@expo/devcert": "^1.1.2",
"@expo/env": "~0.4.1",
"@expo/image-utils": "^0.6.4",
@ -2273,12 +2273,12 @@
"@expo/osascript": "^2.1.5",
"@expo/package-manager": "^1.7.1",
"@expo/plist": "^0.2.1",
"@expo/prebuild-config": "^8.0.25",
"@expo/prebuild-config": "^8.0.26",
"@expo/rudder-sdk-node": "^1.1.1",
"@expo/spawn-async": "^1.7.2",
"@expo/ws-tunnel": "^1.0.1",
"@expo/xcpretty": "^4.3.0",
"@react-native/dev-middleware": "0.76.6",
"@react-native/dev-middleware": "0.76.7",
"@urql/core": "^5.0.6",
"@urql/exchange-retry": "^1.3.0",
"accepts": "^1.3.8",
@ -2360,14 +2360,14 @@
}
},
"node_modules/@expo/config": {
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/@expo/config/-/config-10.0.8.tgz",
"integrity": "sha512-RaKwi8e6PbkMilRexdsxObLMdQwxhY6mlgel+l/eW+IfIw8HEydSU0ERlzYUjlGJxHLHUXe4rC2vw8FEvaowyQ==",
"version": "10.0.9",
"resolved": "https://registry.npmjs.org/@expo/config/-/config-10.0.9.tgz",
"integrity": "sha512-dEw2PKnSjzik3nc2mMzWnLtfiFpd3uGsdBdSl8tEbaRwtMAu74/BOJ1qCgh8slWppikaTcQLQ0xWlqlQU2i4aw==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "~7.10.4",
"@expo/config-plugins": "~9.0.14",
"@expo/config-types": "^52.0.3",
"@expo/config-plugins": "~9.0.15",
"@expo/config-types": "^52.0.4",
"@expo/json-file": "^9.0.1",
"deepmerge": "^4.3.1",
"getenv": "^1.0.0",
@ -2381,12 +2381,12 @@
}
},
"node_modules/@expo/config-plugins": {
"version": "9.0.14",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.14.tgz",
"integrity": "sha512-Lx1ebV95rTFKKQmbu4wMPLz65rKn7mqSpfANdCx+KwRxuLY2JQls8V4h3lQjG6dW8NWf9qV5QaEFAgNB6VMyOQ==",
"version": "9.0.15",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.15.tgz",
"integrity": "sha512-elKY/zIpAJ40RH26iwfyp+hwgeyPgIXX0SrCSOcjeJLsMsCmMac9ewvb+AN8y4k+N7m5lD/dMZupsaateKTFwA==",
"license": "MIT",
"dependencies": {
"@expo/config-types": "^52.0.3",
"@expo/config-types": "^52.0.4",
"@expo/json-file": "~9.0.1",
"@expo/plist": "^0.2.1",
"@expo/sdk-runtime-versions": "^1.0.0",
@ -2415,9 +2415,9 @@
}
},
"node_modules/@expo/config-types": {
"version": "52.0.3",
"resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.3.tgz",
"integrity": "sha512-muxvuARmbysH5OGaiBRlh1Y6vfdmL56JtpXxB+y2Hfhu0ezG1U4FjZYBIacthckZPvnDCcP3xIu1R+eTo7/QFA==",
"version": "52.0.4",
"resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.4.tgz",
"integrity": "sha512-oMGrb2o3niVCIfjnIHFrOoiDA9jGb0lc3G4RI1UiO//KjULBaQr3QTBoKDzZQwMqDV1AgYgSr9mgEcnX3LqhIg==",
"license": "MIT"
},
"node_modules/@expo/config/node_modules/@babel/code-frame": {
@ -2484,9 +2484,9 @@
}
},
"node_modules/@expo/fingerprint": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.11.8.tgz",
"integrity": "sha512-XzDOHr+fTpYPGFe+bIlWtwnLahzuQi7htPgpjLsfL+hKE/2S7wWYlRgB0omScyEBz2yXxzCk3GHJTcG+dffLAg==",
"version": "0.11.9",
"resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.11.9.tgz",
"integrity": "sha512-db99WKkC4JKAnCLL55LOUJ2OvGQOrxOPyQ4stBOgWb5v/OM25rtREqwLtGDpJ5VBvr5DKbXbscWd1yxginq99g==",
"license": "MIT",
"dependencies": {
"@expo/spawn-async": "^1.7.2",
@ -2734,17 +2734,17 @@
}
},
"node_modules/@expo/prebuild-config": {
"version": "8.0.25",
"resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-8.0.25.tgz",
"integrity": "sha512-xYHV8eiydZEDedf2AGaOFRFwcGlaSzrqQH94dwX42urNCU03FO0RUb7yPp4nkb7WNFg5Ov6PDsV7ES+YwzNgYQ==",
"version": "8.0.26",
"resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-8.0.26.tgz",
"integrity": "sha512-SryIKBXPkykUzoCUVP3nt7GclChE74TM8xAstxZ7paXxgcSMN68mNVyUr/zOivLTYQijXltD1I9rNj20Vm5aFw==",
"license": "MIT",
"dependencies": {
"@expo/config": "~10.0.8",
"@expo/config-plugins": "~9.0.14",
"@expo/config-types": "^52.0.3",
"@expo/config-plugins": "~9.0.15",
"@expo/config-types": "^52.0.4",
"@expo/image-utils": "^0.6.4",
"@expo/json-file": "^9.0.1",
"@react-native/normalize-colors": "0.76.6",
"@react-native/normalize-colors": "0.76.7",
"debug": "^4.3.1",
"fs-extra": "^9.0.0",
"resolve-from": "^5.0.0",
@ -2858,9 +2858,9 @@
}
},
"node_modules/@expo/ws-tunnel": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.1.tgz",
"integrity": "sha512-b1VoJfK7u6RVlOoDONyKQQ7JWnHZqa8+bFdWxDmvFUCoiI1ri4MAapSNd308QgADYohSecqIZkITt9hDS722Iw=="
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@expo/ws-tunnel/-/ws-tunnel-1.0.4.tgz",
"integrity": "sha512-spXCVXxbeKOe8YZ9igd+MDfXZe6LeDvFAdILijeTSG+XcxGrZLmqMWWkFKR0nV8lTWZ+NugUT3CoiXmEuKKQ7w=="
},
"node_modules/@expo/xcpretty": {
"version": "4.3.2",
@ -3750,30 +3750,30 @@
"license": "MIT"
},
"node_modules/@react-native/assets-registry": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.6.tgz",
"integrity": "sha512-YI8HoReYiIwdFQs+k9Q9qpFTnsyYikZxgs/UVtVbhKixXDQF6F9LLvj2naOx4cfV+RGybNKxwmDl1vUok/dRFQ==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.76.7.tgz",
"integrity": "sha512-o79whsqL5fbPTUQO9w1FptRd4cw1TaeOrXtQSLQeDrMVAenw/wmsjyPK10VKtvqxa1KNMtWEyfgxcM8CVZVFmg==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/babel-plugin-codegen": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.6.tgz",
"integrity": "sha512-yFC9I/aDBOBz3ZMlqKn2NY/mDUtCksUNZ7AQmBiTAeVTUP0ujEjE0hTOx5Qd+kok7A7hwZEX87HdSgjiJZfr5g==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.7.tgz",
"integrity": "sha512-+8H4DXJREM4l/pwLF/wSVMRzVhzhGDix5jLezNrMD9J1U1AMfV2aSkWA1XuqR7pjPs/Vqf6TaPL7vJMZ4LU05Q==",
"license": "MIT",
"dependencies": {
"@react-native/codegen": "0.76.6"
"@react-native/codegen": "0.76.7"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/babel-preset": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.76.6.tgz",
"integrity": "sha512-ojlVWY6S/VE/nb9hIRetPMTsW9ZmGb2R3dnToEXAtQQDz41eHMHXbkw/k2h0THp6qhas25ruNvn3N5n2o+lBzg==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.76.7.tgz",
"integrity": "sha512-/c5DYZ6y8tyg+g8tgXKndDT7mWnGmkZ9F+T3qNDfoE3Qh7ucrNeC2XWvU9h5pk8eRtj9l4SzF4aO1phzwoibyg==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
@ -3817,7 +3817,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.76.6",
"@react-native/babel-plugin-codegen": "0.76.7",
"babel-plugin-syntax-hermes-parser": "^0.25.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@ -3830,9 +3830,9 @@
}
},
"node_modules/@react-native/codegen": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.76.6.tgz",
"integrity": "sha512-BABb3e5G/+hyQYEYi0AODWh2km2d8ERoASZr6Hv90pVXdUHRYR+yxCatX7vSd9rnDUYndqRTzD0hZWAucPNAKg==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.76.7.tgz",
"integrity": "sha512-FAn585Ll65YvkSrKDyAcsdjHhhAGiMlSTUpHh0x7J5ntudUns+voYms0xMP+pEPt0XuLdjhD7zLIIlAWP407+g==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.25.3",
@ -3873,13 +3873,13 @@
}
},
"node_modules/@react-native/community-cli-plugin": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.6.tgz",
"integrity": "sha512-nETlc/+U5cESVluzzgN0OcVfcoMijGBaDWzOaJhoYUodcuqnqtu75XsSEc7yzlYjwNQG+vF83mu9CQGezruNMA==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.7.tgz",
"integrity": "sha512-lrcsY2WPLCEWU1pjdNV9+Ccj8vCEwCCURZiPa5aqi7lKB4C++1hPrxA8/CWWnTNcQp76DsBKGYqTFj7Ud4aupw==",
"license": "MIT",
"dependencies": {
"@react-native/dev-middleware": "0.76.6",
"@react-native/metro-babel-transformer": "0.76.6",
"@react-native/dev-middleware": "0.76.7",
"@react-native/metro-babel-transformer": "0.76.7",
"chalk": "^4.0.0",
"execa": "^5.1.1",
"invariant": "^2.2.4",
@ -4004,26 +4004,27 @@
"license": "ISC"
},
"node_modules/@react-native/debugger-frontend": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.76.6.tgz",
"integrity": "sha512-kP97xMQjiANi5/lmf8MakS7d8FTJl+BqYHQMqyvNiY+eeWyKnhqW2GL2v3eEUBAuyPBgJGivuuO4RvjZujduJg==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.76.7.tgz",
"integrity": "sha512-89ZtZXt7ZxE94i7T94qzZMhp4Gfcpr/QVpGqEaejAxZD+gvDCH21cYSF+/Rz2ttBazm0rk5MZ0mFqb0Iqp1jmw==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/dev-middleware": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.76.6.tgz",
"integrity": "sha512-1bAyd2/X48Nzb45s5l2omM75vy764odx/UnDs4sJfFCuK+cupU4nRPgl0XWIqgdM/2+fbQ3E4QsVS/WIKTFxvQ==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.76.7.tgz",
"integrity": "sha512-Jsw8g9DyLPnR9yHEGuT09yHZ7M88/GL9CtU9WmyChlBwdXSeE3AmRqLegsV3XcgULQ1fqdemokaOZ/MwLYkjdA==",
"license": "MIT",
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.76.6",
"@react-native/debugger-frontend": "0.76.7",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
"debug": "^2.2.0",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1",
"open": "^7.0.3",
"selfsigned": "^2.4.1",
@ -4059,31 +4060,31 @@
}
},
"node_modules/@react-native/gradle-plugin": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.76.6.tgz",
"integrity": "sha512-sDzpf4eiynryoS6bpYCweGoxSmWgCSx9lzBoxIIW+S6siyGiTaffzZHWCm8mIn9UZsSPlEO37q62ggnR9Zu/OA==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.76.7.tgz",
"integrity": "sha512-gQI6RcrJbigU8xk7F960C5xQIgvbBj20TUvGecD+N2PHfbLpqR+92cj7hz3UcbrCONmTP40WHnbMMJ8P+kLsrA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/js-polyfills": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.6.tgz",
"integrity": "sha512-cDD7FynxWYxHkErZzAJtzPGhJ13JdOgL+R0riTh0hCovOfIUz9ItffdLQv2nx48lnvMTQ+HZXMnGOZnsFCNzQw==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.76.7.tgz",
"integrity": "sha512-+iEikj6c6Zvrg1c3cYMeiPB+5nS8EaIC3jCtP6Muk3qc7c386IymEPM2xycIlfg04DPZvO3D4P2/vaO9/TCnUg==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@react-native/metro-babel-transformer": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.6.tgz",
"integrity": "sha512-xSBi9jPliThu5HRSJvluqUlDOLLEmf34zY/U7RDDjEbZqC0ufPcPS7c5XsSg0GDPiXc7lgjBVesPZsKFkoIBgA==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.7.tgz",
"integrity": "sha512-jDS1wR7q46xY5ah+jF714Mvss9l7+lmwW/tplahZgLKozkYDC8Td5o9TOCgKlv18acw9H1V7zv8ivuRSj8ICPg==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.76.6",
"@react-native/babel-preset": "0.76.7",
"hermes-parser": "0.23.1",
"nullthrows": "^1.1.1"
},
@ -4095,15 +4096,15 @@
}
},
"node_modules/@react-native/normalize-colors": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.6.tgz",
"integrity": "sha512-1n4udXH2Cla31iA/8eLRdhFHpYUYK1NKWCn4m1Sr9L4SarWKAYuRFliK1fcLvPPALCFoFlWvn8I0ekdUOHMzDQ==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.76.7.tgz",
"integrity": "sha512-ST1xxBuYVIXPdD81dR6+tzIgso7m3pa9+6rOBXTh5Xm7KEEFik7tnQX+GydXYMp3wr1gagJjragdXkPnxK6WNg==",
"license": "MIT"
},
"node_modules/@react-native/virtualized-lists": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.6.tgz",
"integrity": "sha512-0HUWVwJbRq1BWFOu11eOWGTSmK9nMHhoMPyoI27wyWcl/nqUx7HOxMbRVq0DsTCyATSMPeF+vZ6o1REapcNWKw==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.76.7.tgz",
"integrity": "sha512-pRUf1jUO8H9Ft04CaWv76t34QI9wY0sydoYlIwEtqXjjMJgmgDoOCAWBjArgn2mk8/rK+u/uicI67ZCYCp1pJw==",
"license": "MIT",
"dependencies": {
"invariant": "^2.2.4",
@ -9565,9 +9566,9 @@
}
},
"node_modules/babel-preset-expo": {
"version": "12.0.6",
"resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-12.0.6.tgz",
"integrity": "sha512-az3H7gDVo0wxNBAFES8h5vLLWE8NPGkD9g5P962hDEOqZUdyPacb9MOzicypeLmcq9zQWr6E3iVtEHoNagCTTQ==",
"version": "12.0.7",
"resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-12.0.7.tgz",
"integrity": "sha512-XT2ZOnonTU343eRnd/UBuqYLxmaB47g+RtLMANMsj/j9XL2kkTk3a6yepLbV4BrACaTf2ddiBZDi+BQ0lgjVaw==",
"license": "MIT",
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.12.9",
@ -9576,7 +9577,7 @@
"@babel/plugin-transform-parameters": "^7.22.15",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@react-native/babel-preset": "0.76.6",
"@react-native/babel-preset": "0.76.7",
"babel-plugin-react-native-web": "~0.19.13",
"react-refresh": "^0.14.2"
},
@ -10341,9 +10342,9 @@
}
},
"node_modules/compression": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz",
"integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==",
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
@ -11170,26 +11171,26 @@
}
},
"node_modules/expo": {
"version": "52.0.30",
"resolved": "https://registry.npmjs.org/expo/-/expo-52.0.30.tgz",
"integrity": "sha512-CQP75djQOMWpNqniV7IBhsUiY/hpVnGWCPfEuYQIsZBEGa/ZQOuOQEVF7MaFeWEOYR1/qaDyv7ON3ON9QPJydg==",
"version": "52.0.33",
"resolved": "https://registry.npmjs.org/expo/-/expo-52.0.33.tgz",
"integrity": "sha512-8lgeJtqw5N1m7910n/pc8cWBT8bzwaNue1mWXElwdhpKSEpPsCYG1sNQ8eqRnC37Y8aj6i6TrNFmSP++7zUS3Q==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "0.22.12",
"@expo/config": "~10.0.8",
"@expo/config-plugins": "~9.0.14",
"@expo/fingerprint": "0.11.8",
"@expo/cli": "0.22.15",
"@expo/config": "~10.0.9",
"@expo/config-plugins": "~9.0.15",
"@expo/fingerprint": "0.11.9",
"@expo/metro-config": "0.19.9",
"@expo/vector-icons": "^14.0.0",
"babel-preset-expo": "~12.0.6",
"babel-preset-expo": "~12.0.7",
"expo-asset": "~11.0.3",
"expo-constants": "~17.0.5",
"expo-file-system": "~18.0.9",
"expo-file-system": "~18.0.10",
"expo-font": "~13.0.3",
"expo-keep-awake": "~14.0.2",
"expo-modules-autolinking": "2.0.7",
"expo-modules-core": "2.2.0",
"expo-modules-core": "2.2.1",
"fbemitter": "^3.0.0",
"web-streams-polyfill": "^3.3.2",
"whatwg-url-without-unicode": "8.0.0-3"
@ -11248,9 +11249,9 @@
}
},
"node_modules/expo-file-system": {
"version": "18.0.9",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.9.tgz",
"integrity": "sha512-DPuAyLP1012SFC92LKNJpLJw3QBbyxfYXNj9nTOdq299MYTf8kyOjLuVNMQqg1jX+yGiRDp0lHFoCgln0xsZYA==",
"version": "18.0.10",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.10.tgz",
"integrity": "sha512-+GnxkI+J9tOzUQMx+uIOLBEBsO2meyoYHxd87m9oT9M//BpepYqI1AvYBH8YM4dgr9HaeaeLr7z5XFVqfL8tWg==",
"license": "MIT",
"dependencies": {
"web-streams-polyfill": "^3.3.2"
@ -11353,21 +11354,21 @@
}
},
"node_modules/expo-modules-core": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.2.0.tgz",
"integrity": "sha512-mOFEHIe6jZ7G5pYUVSQ2Ghs3CUr9Uz6DOh4JI+4PsTf0gmEvMmMEOrxirS89jRWQjXPJ7QaGBK0CJrZlj/Sdeg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-2.2.1.tgz",
"integrity": "sha512-pxQpfgevHiy5EVRDE0w3mrVu0UTNHELr4GDXEQWAE1g4JVS5ZGNq/Gu2VGgFbBP18KGPNB+gEy8UFI48ADbiuw==",
"license": "MIT",
"dependencies": {
"invariant": "^2.2.4"
}
},
"node_modules/expo-navigation-bar": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/expo-navigation-bar/-/expo-navigation-bar-4.0.7.tgz",
"integrity": "sha512-0dU6nU4XzLBFfkX7KTeYj6qX/3W/peYV9LjKymBsRPY0/uVgEcgFgS+omQAZE6DVD4Duy3GcT+91ozCUmvEEdA==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/expo-navigation-bar/-/expo-navigation-bar-4.0.8.tgz",
"integrity": "sha512-rmQkCCwfYeR29GTPwoNuN7eWE8bYCW5UGJ/5CnZQxIaiBmOeepoDXu50AzEM5ZFPMK6J12nvyu1yj0ujbLUpsQ==",
"license": "MIT",
"dependencies": {
"@react-native/normalize-colors": "0.76.6",
"@react-native/normalize-colors": "0.76.7",
"debug": "^4.3.2"
},
"peerDependencies": {
@ -11452,12 +11453,12 @@
}
},
"node_modules/expo-system-ui": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-4.0.7.tgz",
"integrity": "sha512-x1VDoE7J8m4wxTgWyUBEYqsf1KabIg64dOLzYiZjg0cWOE6o6kX2Mg6n3abVWEEC01WhZBoo9+Urcce/6ZJ3tg==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-4.0.8.tgz",
"integrity": "sha512-0AmWXJ3ObwMYxi2YGagwRQikydoUZJXLeK4A0FY1PsZpnlorSQ4IAfEVS38JmA54tf5CpP4TjBp5ZVEjRyv1rw==",
"license": "MIT",
"dependencies": {
"@react-native/normalize-colors": "0.76.6",
"@react-native/normalize-colors": "0.76.7",
"debug": "^4.3.2"
},
"peerDependencies": {
@ -11472,9 +11473,9 @@
}
},
"node_modules/exponential-backoff": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz",
"integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
"integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==",
"license": "Apache-2.0"
},
"node_modules/fast-deep-equal": {
@ -15361,19 +15362,19 @@
"license": "MIT"
},
"node_modules/react-native": {
"version": "0.76.6",
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.6.tgz",
"integrity": "sha512-AsRi+ud6v6ADH7ZtSOY42kRB4nbM0KtSu450pGO4pDudl4AEK/AF96ai88snb2/VJJSGGa/49QyJVFXxz/qoFg==",
"version": "0.76.7",
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.7.tgz",
"integrity": "sha512-GPJcQeO3qUi1MvuhsC2DC6tH8gJQ4uc4JWPORrdeuCGFWE3QLsN8/hiChTEvJREHLfQSV61YPI8gIOtAQ8c37g==",
"license": "MIT",
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/assets-registry": "0.76.6",
"@react-native/codegen": "0.76.6",
"@react-native/community-cli-plugin": "0.76.6",
"@react-native/gradle-plugin": "0.76.6",
"@react-native/js-polyfills": "0.76.6",
"@react-native/normalize-colors": "0.76.6",
"@react-native/virtualized-lists": "0.76.6",
"@react-native/assets-registry": "0.76.7",
"@react-native/codegen": "0.76.7",
"@react-native/community-cli-plugin": "0.76.7",
"@react-native/gradle-plugin": "0.76.7",
"@react-native/js-polyfills": "0.76.7",
"@react-native/normalize-colors": "0.76.7",
"@react-native/virtualized-lists": "0.76.7",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
@ -17183,9 +17184,9 @@
}
},
"node_modules/terser": {
"version": "5.38.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.38.0.tgz",
"integrity": "sha512-a4GD5R1TjEeuCT6ZRiYMHmIf7okbCPEuhQET8bczV6FrQMMlFXA1n+G0KKjdlFCm3TEHV77GxfZB3vZSUQGFpg==",
"version": "5.38.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.38.2.tgz",
"integrity": "sha512-w8CXxxbFA5zfNsR/i8HZq5bvn18AK0O9jj7hyo1YqkovLxEFa0uP0LCVGZRqiRaKRFxXhELBp8SteeAjEnfeJg==",
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",

View File

@ -12,6 +12,14 @@
"clean": "rm -rf .expo node_modules",
"postinstall": "npx tailwindcss -i ./global.css -o ./node_modules/.cache/nativewind/global.css"
},
"expo": {
"doctor": {
"reactNativeDirectoryCheck": {
"exclude": ["lucide-react-native"],
"listUnknownPackages": false
}
}
},
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.6",
"@react-navigation/material-top-tabs": "^7.1.0",
@ -45,18 +53,18 @@
"@rn-primitives/types": "~1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"expo": "^52.0.25",
"expo": "~52.0.33",
"expo-linking": "~7.0.4",
"expo-navigation-bar": "~4.0.7",
"expo-navigation-bar": "~4.0.8",
"expo-router": "~4.0.16",
"expo-splash-screen": "~0.29.20",
"expo-status-bar": "~2.0.1",
"expo-system-ui": "~4.0.7",
"expo-system-ui": "~4.0.8",
"lucide-react-native": "^0.378.0",
"nativewind": "^4.1.23",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.6",
"react-native": "0.76.7",
"react-native-gesture-handler": "~2.20.2",
"react-native-pager-view": "^6.5.1",
"react-native-reanimated": "~3.16.1",

View File

@ -15,5 +15,5 @@
".expo/types/**/*.ts",
"expo-env.d.ts",
"nativewind-env.d.ts"
]
, "jest.config.js", "jest.setup.js" ]
}