Merge pull request #1 from Sosokker/home_page

Home page
This commit is contained in:
Sirin Puenggun 2025-05-08 21:49:37 +07:00 committed by GitHub
commit 2b7973ab81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1025 additions and 734 deletions

View File

@ -1,21 +1,30 @@
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
import { Tabs } from "expo-router"; import { Tabs } from "expo-router";
import { HapticTab } from "@/components/HapticTab";
import { IconSymbol } from "@/components/ui/IconSymbol";
export default function TabLayout() { export default function TabLayout() {
return ( return (
<Tabs <Tabs
screenOptions={{ screenOptions={{
tabBarActiveTintColor: "#FF0000", // Red active color
headerShown: false,
tabBarButton: HapticTab,
tabBarStyle: { tabBarStyle: {
backgroundColor: "#FFCC00", // Yellow background backgroundColor: "#ffd60a",
height: 60, height: 70,
paddingBottom: 5, borderTopLeftRadius: 0,
borderTopRightRadius: 0,
position: "absolute",
bottom: 0,
left: 0,
right: 0,
elevation: 0,
borderTopWidth: 0, borderTopWidth: 0,
}, },
tabBarActiveTintColor: "#bb0718",
tabBarInactiveTintColor: "#bb0718",
tabBarShowLabel: true,
tabBarLabelStyle: {
fontSize: 12,
fontWeight: "500",
},
headerShown: false,
}} }}
> >
<Tabs.Screen <Tabs.Screen
@ -23,34 +32,45 @@ export default function TabLayout() {
options={{ options={{
title: "Home", title: "Home",
tabBarIcon: ({ color }) => ( tabBarIcon: ({ color }) => (
<IconSymbol size={28} name="house.fill" color={color} /> <Ionicons name="home-outline" size={24} color={color} />
), ),
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="recipes" name="recipes"
options={{ options={{
title: "Recipes", title: "Recipes",
tabBarIcon: ({ color }) => ( tabBarIcon: ({ color }) => (
<IconSymbol size={28} name="doc.text.fill" color={color} /> <MaterialCommunityIcons
name="notebook-outline"
size={24}
color={color}
/>
), ),
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="forum" name="forum"
options={{ options={{
title: "Forum", title: "Forum",
tabBarIcon: ({ color }) => ( tabBarIcon: ({ color }) => (
<IconSymbol size={28} name="hand.thumbsup.fill" color={color} /> <MaterialCommunityIcons
name="forum-outline"
size={24}
color={color}
/>
), ),
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="profile" name="profile"
options={{ options={{
title: "Profile", title: "Profile",
tabBarIcon: ({ color }) => ( tabBarIcon: ({ color }) => (
<IconSymbol size={28} name="person.fill" color={color} /> <Ionicons name="person-outline" size={24} color={color} />
), ),
}} }}
/> />

View File

@ -2,7 +2,6 @@ import { IconSymbol } from "@/components/ui/IconSymbol";
import { Image } from "expo-image"; import { Image } from "expo-image";
import { import {
ScrollView, ScrollView,
StyleSheet,
Text, Text,
TextInput, TextInput,
TouchableOpacity, TouchableOpacity,
@ -12,63 +11,63 @@ import { SafeAreaView } from "react-native-safe-area-context";
export default function ForumScreen() { export default function ForumScreen() {
return ( return (
<SafeAreaView style={styles.container} edges={["top"]}> <SafeAreaView className="flex-1 bg-white" edges={["top"]}>
<ScrollView style={styles.scrollView}> <ScrollView className="flex-1">
{/* Search Bar */} {/* Search Bar */}
<View style={styles.searchContainer}> <View className="flex-row items-center mx-4 mt-2 mb-4 px-3 h-10 bg-white rounded-full border border-gray-300">
<IconSymbol name="magnifyingglass" size={20} color="#FF0000" /> <IconSymbol name="magnifyingglass" size={20} color="#FF0000" />
<TextInput <TextInput
style={styles.searchInput} className="flex-1 ml-2 text-[#333]"
placeholder="Search" placeholder="Search"
placeholderTextColor="#FF0000" placeholderTextColor="#FF0000"
/> />
</View> </View>
{/* Category Filters */} {/* Category Filters */}
<View style={styles.categoryContainer}> <View className="flex-row justify-between mx-4 mb-4">
<TouchableOpacity style={styles.categoryButton}> <TouchableOpacity className="bg-[#FFCC00] py-3 px-4 rounded-xl flex-1 mx-1 items-center">
<Text style={styles.categoryText}>Main dish</Text> <Text className="font-bold text-[#333]">Main dish</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.categoryButton}> <TouchableOpacity className="bg-[#FFCC00] py-3 px-4 rounded-xl flex-1 mx-1 items-center">
<Text style={styles.categoryText}>Dessert</Text> <Text className="font-bold text-[#333]">Dessert</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.categoryButton}> <TouchableOpacity className="bg-[#FFCC00] py-3 px-4 rounded-xl flex-1 mx-1 items-center">
<Text style={styles.categoryText}>Appetite</Text> <Text className="font-bold text-[#333]">Appetite</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{/* Filter Options */} {/* Filter Options */}
<View style={styles.filterContainer}> <View className="flex-row mx-4 mb-4">
<TouchableOpacity style={styles.filterButton}> <TouchableOpacity className="bg-red-600 py-2 px-3 rounded-full mr-2 flex-row items-center">
<Text style={styles.filterText}>Rating</Text> <Text className="text-white font-bold mr-1">Rating</Text>
<IconSymbol name="star.fill" size={16} color="#FFCC00" /> <IconSymbol name="star.fill" size={16} color="#FFCC00" />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.filterButton}> <TouchableOpacity className="bg-red-600 py-2 px-3 rounded-full mr-2 flex-row items-center">
<Text style={styles.filterText}>Newest</Text> <Text className="text-white font-bold mr-1">Newest</Text>
<IconSymbol name="calendar" size={16} color="#FFFFFF" /> <IconSymbol name="calendar" size={16} color="#FFFFFF" />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.filterButton}> <TouchableOpacity className="bg-red-600 py-2 px-3 rounded-full mr-2 flex-row items-center">
<Text style={styles.filterText}>Best</Text> <Text className="text-white font-bold mr-1">Best</Text>
<IconSymbol name="flame.fill" size={16} color="#FFCC00" /> <IconSymbol name="flame.fill" size={16} color="#FFCC00" />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{/* Post */} {/* Post */}
<View style={styles.postContainer}> <View className="mx-4 mb-4 bg-white rounded-xl overflow-hidden border border-[#EEEEEE]">
{/* User Info */} {/* User Info */}
<View style={styles.userInfoContainer}> <View className="flex-row justify-between items-center px-3 py-2">
<View style={styles.userInfo}> <View className="flex-row items-center">
<View style={styles.userAvatar}> <View className="w-8 h-8 rounded-full bg-gray-200 justify-center items-center mr-2">
<IconSymbol <IconSymbol
name="person.circle.fill" name="person.circle.fill"
size={24} size={24}
color="#888888" color="#888888"
/> />
</View> </View>
<Text style={styles.userName}>Mr. Chef</Text> <Text className="font-bold text-[#333]">Mr. Chef</Text>
</View> </View>
<View style={styles.ratingContainer}> <View className="flex-row items-center">
<Text style={styles.ratingText}>4.2</Text> <Text className="mr-1 font-bold text-[#333]">4.2</Text>
<IconSymbol name="star.fill" size={16} color="#FFCC00" /> <IconSymbol name="star.fill" size={16} color="#FFCC00" />
</View> </View>
</View> </View>
@ -76,14 +75,16 @@ export default function ForumScreen() {
{/* Post Image */} {/* Post Image */}
<Image <Image
source={require("@/assets/images/placeholder-food.jpg")} source={require("@/assets/images/placeholder-food.jpg")}
style={styles.postImage} className="w-full h-[200px]"
contentFit="cover" resizeMode="cover"
/> />
{/* Post Content */} {/* Post Content */}
<View style={styles.postContent}> <View className="p-3">
<Text style={styles.postTitle}>Kajjecaw</Text> <Text className="text-base font-bold mb-1 text-[#333]">
<Text style={styles.postDescription}> Kajjecaw
</Text>
<Text className="text-[#666] text-sm">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut at Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut at
hendrerit enim. Etiam lacinia mi nec nunc ornare, vitae tempus leo hendrerit enim. Etiam lacinia mi nec nunc ornare, vitae tempus leo
aliquet... aliquet...
@ -91,24 +92,24 @@ export default function ForumScreen() {
</View> </View>
{/* Post Actions */} {/* Post Actions */}
<View style={styles.postActions}> <View className="flex-row border-t border-[#EEEEEE] py-2 px-3">
<TouchableOpacity style={styles.actionButton}> <TouchableOpacity className="flex-row items-center mr-4">
<IconSymbol <IconSymbol
name="arrowshape.turn.up.left.fill" name="arrowshape.turn.up.left.fill"
size={16} size={16}
color="#888888" color="#888888"
/> />
<Text style={styles.actionText}>3</Text> <Text className="ml-1 text-[#888]">3</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.actionButton}> <TouchableOpacity className="flex-row items-center mr-4">
<IconSymbol name="text.bubble.fill" size={16} color="#888888" /> <IconSymbol name="text.bubble.fill" size={16} color="#888888" />
<Text style={styles.actionText}>2</Text> <Text className="ml-1 text-[#888]">2</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.actionButton}> <TouchableOpacity className="flex-row items-center mr-4">
<IconSymbol name="heart.fill" size={16} color="#888888" /> <IconSymbol name="heart.fill" size={16} color="#888888" />
<Text style={styles.actionText}>2</Text> <Text className="ml-1 text-[#888]">2</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.actionButton}> <TouchableOpacity className="flex-row items-center mr-4">
<IconSymbol name="bookmark.fill" size={16} color="#888888" /> <IconSymbol name="bookmark.fill" size={16} color="#888888" />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@ -117,144 +118,3 @@ export default function ForumScreen() {
</SafeAreaView> </SafeAreaView>
); );
} }
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#FFFFFF",
},
scrollView: {
flex: 1,
},
searchContainer: {
flexDirection: "row",
alignItems: "center",
marginHorizontal: 16,
marginTop: 10,
marginBottom: 16,
paddingHorizontal: 12,
height: 40,
backgroundColor: "#FFFFFF",
borderRadius: 20,
borderWidth: 1,
borderColor: "#DDDDDD",
},
searchInput: {
flex: 1,
marginLeft: 8,
color: "#333333",
},
categoryContainer: {
flexDirection: "row",
justifyContent: "space-between",
marginHorizontal: 16,
marginBottom: 16,
},
categoryButton: {
backgroundColor: "#FFCC00",
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 12,
flex: 1,
marginHorizontal: 4,
alignItems: "center",
},
categoryText: {
fontWeight: "bold",
color: "#333333",
},
filterContainer: {
flexDirection: "row",
marginHorizontal: 16,
marginBottom: 16,
},
filterButton: {
backgroundColor: "#FF0000",
paddingVertical: 8,
paddingHorizontal: 12,
borderRadius: 20,
marginRight: 8,
flexDirection: "row",
alignItems: "center",
},
filterText: {
color: "#FFFFFF",
fontWeight: "bold",
marginRight: 4,
},
postContainer: {
marginHorizontal: 16,
marginBottom: 16,
backgroundColor: "#FFFFFF",
borderRadius: 12,
overflow: "hidden",
borderWidth: 1,
borderColor: "#EEEEEE",
},
userInfoContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 12,
paddingVertical: 8,
},
userInfo: {
flexDirection: "row",
alignItems: "center",
},
userAvatar: {
width: 32,
height: 32,
borderRadius: 16,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#F5F5F5",
},
userName: {
marginLeft: 8,
fontWeight: "bold",
color: "#333333",
},
ratingContainer: {
flexDirection: "row",
alignItems: "center",
},
ratingText: {
marginRight: 4,
fontWeight: "bold",
color: "#333333",
},
postImage: {
width: "100%",
height: 200,
},
postContent: {
padding: 12,
},
postTitle: {
fontSize: 16,
fontWeight: "bold",
marginBottom: 4,
color: "#333333",
},
postDescription: {
color: "#666666",
fontSize: 14,
},
postActions: {
flexDirection: "row",
borderTopWidth: 1,
borderTopColor: "#EEEEEE",
paddingVertical: 8,
paddingHorizontal: 12,
},
actionButton: {
flexDirection: "row",
alignItems: "center",
marginRight: 16,
},
actionText: {
marginLeft: 4,
color: "#666666",
},
});

View File

@ -1,113 +1,270 @@
import { Image } from "expo-image"; import { IconSymbol } from "@/components/ui/IconSymbol";
import { Feather, FontAwesome, Ionicons } from "@expo/vector-icons";
import * as ImagePicker from "expo-image-picker";
import { router } from "expo-router"; import { router } from "expo-router";
import React, { useState } from "react";
import { import {
Alert,
Image,
SafeAreaView,
ScrollView, ScrollView,
StyleSheet, StatusBar,
Text, Text,
TextInput, TextInput,
TouchableOpacity, TouchableOpacity,
View, View,
} from "react-native"; } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { IconSymbol } from "@/components/ui/IconSymbol"; // Sample recipe data
const foodHighlights = [
{
id: 1,
name: "Pad Kra Pao Moo Sab with Eggs",
image: require("@/assets/images/food/padkrapao.jpg"),
description: "Thai stir-fry with ground pork and holy basil",
time: "30 Mins",
calories: "520 kcal",
},
{
id: 2,
name: "Jjajangmyeon",
image: require("@/assets/images/food/jjajangmyeon.jpg"),
description: "Korean black bean noodles",
time: "45 Mins",
calories: "650 kcal",
},
{
id: 3,
name: "Ramen",
image: require("@/assets/images/food/ramen.jpg"),
description: "Japanese noodle soup",
time: "60 Mins",
calories: "480 kcal",
},
{
id: 4,
name: "Beef Wellington",
image: require("@/assets/images/food/beef.jpg"),
description: "Tender beef wrapped in puff pastry",
time: "90 Mins",
calories: "750 kcal",
},
];
const navigateToFoodDetail = (foodId: string) => {
router.push({ pathname: "/food/[id]", params: { id: foodId } });
};
export default function HomeScreen() { export default function HomeScreen() {
const foodHighlights = [ const [searchQuery, setSearchQuery] = useState("");
{ const [filteredRecipes, setFilteredRecipes] = useState(foodHighlights);
id: 1,
name: "Pad Kra Pao Moo Sab with Eggs",
image: require("@/assets/images/food/padkrapao.jpg"),
description: "Thai stir-fry with ground pork and holy basil",
time: "30 Mins",
calories: "520 kcal",
},
{
id: 2,
name: "Jjajangmyeon",
image: require("@/assets/images/food/jjajangmyeon.jpg"),
description: "Korean black bean noodles",
time: "45 Mins",
calories: "650 kcal",
},
{
id: 3,
name: "Ramen",
image: require("@/assets/images/food/ramen.jpg"),
description: "Japanese noodle soup",
time: "60 Mins",
calories: "480 kcal",
},
{
id: 4,
name: "Beef Wellington",
image: require("@/assets/images/food/beef.jpg"),
description: "Tender beef wrapped in puff pastry",
time: "90 Mins",
calories: "750 kcal",
},
];
const navigateToFoodDetail = (foodId: string) => { // Handle search
router.push({ pathname: "/food/[id]", params: { id: foodId } }); const handleSearch = (text: string): void => {
setSearchQuery(text);
if (text) {
const filtered = foodHighlights.filter((food) =>
food.name.toLowerCase().includes(text.toLowerCase())
);
setFilteredRecipes(filtered);
} else {
setFilteredRecipes(foodHighlights);
}
};
// Handle camera
const takePhoto = async () => {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission needed",
"Please grant camera permissions to use this feature."
);
return;
}
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.canceled) {
// Navigate to recipe detail with the captured image
router.push({
pathname: "/recipe-detail",
params: {
title: "My New Recipe",
image: result.assets[0].uri,
},
});
}
};
// Handle gallery
const pickImage = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission needed",
"Please grant media library permissions to use this feature."
);
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
if (!result.canceled) {
// Navigate to recipe detail with the selected image
router.push({
pathname: "/recipe-detail",
params: {
title: "My New Recipe",
image: result.assets[0].uri,
},
});
}
};
// Navigate to recipe detail
interface Recipe {
id: number;
title: string;
image: string;
color: string;
}
const goToRecipeDetail = (recipe: Recipe): void => {
router.push({
pathname: "/recipe-detail",
params: {
title: recipe.title,
image: recipe.image,
},
});
}; };
return ( return (
<SafeAreaView style={styles.container} edges={["top"]}> <SafeAreaView className="flex-1 bg-white">
<ScrollView style={styles.scrollView}> <StatusBar barStyle="dark-content" />
{/* Header */}
<View style={styles.header}>
<Text style={styles.greeting}>Hi! Mr. Chef</Text>
<TouchableOpacity style={styles.scanButton}>
<IconSymbol name="qrcode.viewfinder" size={24} color="#333333" />
</TouchableOpacity>
</View>
{/* Hero Section */} {/* Header - Fixed at top */}
<View style={styles.heroContainer}> <View className="flex-row justify-between items-center px-6 pt-4 pb-2">
<View style={styles.heroContent}> <Text className="text-3xl font-bold">Hi! Mr. Chef</Text>
<Image <View className="bg-[#ffd60a] p-3 rounded-lg">
source={require("@/assets/images/notebook-orange.png")} <Ionicons name="settings-outline" size={24} color="black" />
style={styles.heroImage} </View>
contentFit="contain" </View>
{/* Scrollable Content */}
<ScrollView
className="flex-1"
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: 100 }}
>
{/* Show your dishes */}
<View className="px-6 mb-6">
<View className="flex-row items-center mb-4">
<Text className="text-2xl font-bold mr-2">Show your dishes</Text>
<Feather name="wifi" size={20} color="black" />
</View>
<View className="bg-white border border-gray-300 rounded-full mb-6 flex-row items-center px-4 py-3">
<TextInput
className="flex-1"
placeholder="Search..."
value={searchQuery}
onChangeText={handleSearch}
/> />
<View className="bg-[#ffd60a] p-2 rounded-full">
<Feather name="send" size={20} color="black" />
</View>
</View>
<View className="flex-row justify-between">
<TouchableOpacity
className="bg-[#ffd60a] p-4 rounded-xl w-[48%]"
onPress={takePhoto}
>
<View className="items-center">
<FontAwesome name="camera" size={24} color="black" />
<Text className="text-lg font-bold mt-2">From Camera</Text>
<Text className="text-sm text-gray-700">
Straight from Camera
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
className="bg-[#f9be25] p-4 rounded-xl w-[48%]"
onPress={pickImage}
>
<View className="items-center">
<Feather name="image" size={24} color="black" />
<Text className="text-lg font-bold mt-2">From Gallery</Text>
<Text className="text-sm text-gray-700">
Straight from Gallery
</Text>
</View>
</TouchableOpacity>
</View> </View>
</View> </View>
{/* Food Highlights Section */} {/* Food Highlights Section */}
<View style={styles.sectionContainer}> <View className="mx-4 mb-6">
<View style={styles.sectionHeader}> <View className="flex-row items-center mb-3">
<Text style={styles.sectionTitle}>Food Highlights</Text> <Text className="text-lg font-bold text-[#333] mr-2">
Food Highlights
</Text>
<IconSymbol name="star.fill" size={16} color="#FFCC00" /> <IconSymbol name="star.fill" size={16} color="#FFCC00" />
</View> </View>
<View className="w-full">
<View style={styles.foodHighlightsContainer}>
{foodHighlights.map((food) => ( {foodHighlights.map((food) => (
<TouchableOpacity <TouchableOpacity
key={food.id} key={food.id}
style={styles.foodCard} className="flex-row bg-white rounded-xl mb-3 shadow-sm overflow-hidden"
style={{
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2,
}}
onPress={() => navigateToFoodDetail(String(food.id))} onPress={() => navigateToFoodDetail(String(food.id))}
> >
<Image <Image
source={food.image} source={food.image}
style={styles.foodImage} className="w-[88px] h-[88px] rounded-l-xl"
contentFit="cover" resizeMode="cover"
/> />
<View style={styles.foodCardContent}> <View className="flex-1 p-3 justify-between">
<Text style={styles.foodCardTitle} numberOfLines={1}> <Text
className="text-base font-bold text-[#333] mb-1"
numberOfLines={1}
>
{food.name} {food.name}
</Text> </Text>
<Text style={styles.foodCardDescription} numberOfLines={1}> <Text className="text-sm text-[#666] mb-2" numberOfLines={1}>
{food.description} {food.description}
</Text> </Text>
<View style={styles.foodCardMeta}> <View className="flex-row justify-between">
<View style={styles.foodCardMetaItem}> <View className="flex-row items-center">
<IconSymbol name="clock" size={12} color="#666666" /> <IconSymbol name="clock" size={12} color="#666666" />
<Text style={styles.foodCardMetaText}>{food.time}</Text> <Text className="text-xs text-[#666] ml-1">
{food.time}
</Text>
</View> </View>
<View style={styles.foodCardMetaItem}> <View className="flex-row items-center">
<IconSymbol name="flame" size={12} color="#666666" /> <IconSymbol name="flame" size={12} color="#666666" />
<Text style={styles.foodCardMetaText}> <Text className="text-xs text-[#666] ml-1">
{food.calories} {food.calories}
</Text> </Text>
</View> </View>
@ -118,204 +275,9 @@ export default function HomeScreen() {
</View> </View>
</View> </View>
{/* Show your dishes Section */} {/* Extra space at bottom */}
<View style={styles.sectionContainer}> <View className="h-20"></View>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Show your dishes</Text>
<IconSymbol name="chevron.down" size={16} color="#333333" />
</View>
<View style={styles.searchContainer}>
<TextInput style={styles.searchInput} placeholder="Search..." />
<TouchableOpacity style={styles.searchButton}>
<IconSymbol name="arrow.right" size={16} color="#333333" />
</TouchableOpacity>
</View>
<View style={styles.uploadOptions}>
<TouchableOpacity style={styles.uploadOption}>
<IconSymbol name="camera.fill" size={24} color="#333333" />
<Text style={styles.uploadOptionTitle}>From Camera</Text>
<Text style={styles.uploadOptionSubtitle}>
Snap it from Camera
</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.uploadOption, styles.orangeOption]}
>
<IconSymbol name="photo.fill" size={24} color="#333333" />
<Text style={styles.uploadOptionTitle}>From Gallery</Text>
<Text style={styles.uploadOptionSubtitle}>
Select from Gallery
</Text>
</TouchableOpacity>
</View>
</View>
</ScrollView> </ScrollView>
</SafeAreaView> </SafeAreaView>
); );
} }
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#FFFFFF",
},
scrollView: {
flex: 1,
},
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 16,
paddingVertical: 12,
},
greeting: {
fontSize: 20,
fontWeight: "bold",
color: "#333333",
},
scanButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: "#FFCC00",
justifyContent: "center",
alignItems: "center",
},
heroContainer: {
height: 180,
marginHorizontal: 16,
marginVertical: 16,
borderRadius: 16,
backgroundColor: "#FFF3D9", // Light yellow/orange gradient
overflow: "hidden",
justifyContent: "center",
alignItems: "center",
},
heroContent: {
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
position: "relative",
},
heroImage: {
width: 120,
height: 120,
},
sectionContainer: {
marginHorizontal: 16,
marginBottom: 24,
},
sectionHeader: {
flexDirection: "row",
alignItems: "center",
marginBottom: 12,
},
sectionTitle: {
fontSize: 18,
fontWeight: "bold",
color: "#333333",
marginRight: 8,
},
foodHighlightsContainer: {
width: "100%",
},
foodCard: {
flexDirection: "row",
backgroundColor: "#FFFFFF",
borderRadius: 12,
marginBottom: 12,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2,
overflow: "hidden",
},
foodImage: {
width: 100,
height: 100,
},
foodCardContent: {
flex: 1,
padding: 12,
justifyContent: "space-between",
},
foodCardTitle: {
fontSize: 16,
fontWeight: "bold",
color: "#333333",
marginBottom: 4,
},
foodCardDescription: {
fontSize: 14,
color: "#666666",
marginBottom: 8,
},
foodCardMeta: {
flexDirection: "row",
justifyContent: "space-between",
},
foodCardMetaItem: {
flexDirection: "row",
alignItems: "center",
},
foodCardMetaText: {
fontSize: 12,
color: "#666666",
marginLeft: 4,
},
searchContainer: {
flexDirection: "row",
alignItems: "center",
marginBottom: 16,
borderWidth: 1,
borderColor: "#DDDDDD",
borderRadius: 24,
paddingHorizontal: 12,
height: 48,
},
searchInput: {
flex: 1,
height: "100%",
},
searchButton: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: "#FFCC00",
justifyContent: "center",
alignItems: "center",
},
uploadOptions: {
flexDirection: "row",
justifyContent: "space-between",
},
uploadOption: {
flex: 1,
height: 100,
backgroundColor: "#FFCC00",
borderRadius: 12,
marginRight: 8,
padding: 12,
justifyContent: "center",
alignItems: "center",
},
orangeOption: {
backgroundColor: "#FFA500", // Darker orange
marginRight: 0,
},
uploadOptionTitle: {
fontWeight: "bold",
color: "#333333",
marginTop: 8,
},
uploadOptionSubtitle: {
fontSize: 12,
color: "#333333",
opacity: 0.8,
},
});

View File

@ -2,13 +2,7 @@
import { Image } from "expo-image"; import { Image } from "expo-image";
import { useState } from "react"; import { useState } from "react";
import { import { ScrollView, Text, TouchableOpacity, View } from "react-native";
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context"; import { SafeAreaView } from "react-native-safe-area-context";
export default function ProfileScreen() { export default function ProfileScreen() {
@ -66,47 +60,54 @@ export default function ProfileScreen() {
]; ];
return ( return (
<SafeAreaView style={styles.container} edges={["top"]}> <SafeAreaView className="flex-1 bg-white" edges={["top"]}>
<ScrollView style={styles.scrollView}> <ScrollView className="flex-1">
{/* Profile Header */} {/* Profile Header */}
<View style={styles.profileHeader}> <View className="items-center py-6">
<View style={styles.avatarContainer}> <View className="w-[100px] h-[100px] rounded-full border border-gray-300 justify-center items-center mb-3">
<View style={styles.avatar}> <View className="w-[96px] h-[96px] rounded-full bg-gray-100 justify-center items-center">
<Text style={styles.avatarPlaceholder}>👨🍳</Text> <Text className="text-5xl">👨🍳</Text>
</View> </View>
</View> </View>
<Text style={styles.username}>Mr. Chef</Text> <Text className="text-xl font-bold mb-3">Mr. Chef</Text>
<TouchableOpacity style={styles.editButton}> <TouchableOpacity className="bg-red-600 py-2 px-10 rounded-lg">
<Text style={styles.editButtonText}>Edit</Text> <Text className="text-white font-bold">Edit</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{/* Tab Navigation */} {/* Tab Navigation */}
<View style={styles.tabContainer}> <View className="flex-row justify-around py-3">
{["Repost", "Likes", "Bookmark"].map((tab) => ( {["Repost", "Likes", "Bookmark"].map((tab) => (
<TouchableOpacity <TouchableOpacity
key={tab} key={tab}
style={[styles.tab, activeTab === tab && styles.activeTab]} className={`py-2 px-4 ${
activeTab === tab ? "border-b-2 border-[#333]" : ""
}`}
onPress={() => setActiveTab(tab)} onPress={() => setActiveTab(tab)}
> >
<Text style={styles.tabText}>{tab}</Text> <Text className="font-medium">{tab}</Text>
</TouchableOpacity> </TouchableOpacity>
))} ))}
</View> </View>
<View style={styles.divider} /> <View className="h-px bg-[#EEEEEE] mx-4" />
{/* Food Grid */} {/* Food Grid */}
<View style={styles.foodGrid}> <View className="flex-row flex-wrap p-2">
{foodItems.map((item, index) => ( {foodItems.map((item, index) => (
<View key={`${item.id}-${index}`} style={styles.foodItem}> <View key={`${item.id}-${index}`} className="w-1/2 p-2 relative">
<Image <Image
source={item.image} source={item.image}
style={styles.foodImage} className="w-full h-[120px] rounded-lg"
contentFit="cover" resizeMode="cover"
/> />
<View style={[styles.foodLabel, { backgroundColor: item.color }]}> <View
<Text style={styles.foodLabelText}>{item.name}</Text> className="absolute bottom-4 left-4 py-1 px-2 rounded bg-opacity-90"
style={{ backgroundColor: item.color }}
>
<Text className="text-[#333] font-bold text-xs">
{item.name}
</Text>
</View> </View>
</View> </View>
))} ))}
@ -115,102 +116,3 @@ export default function ProfileScreen() {
</SafeAreaView> </SafeAreaView>
); );
} }
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#FFFFFF",
},
scrollView: {
flex: 1,
},
profileHeader: {
alignItems: "center",
paddingVertical: 24,
},
avatarContainer: {
width: 100,
height: 100,
borderRadius: 50,
borderWidth: 1,
borderColor: "#DDDDDD",
justifyContent: "center",
alignItems: "center",
marginBottom: 12,
},
avatar: {
width: 96,
height: 96,
borderRadius: 48,
backgroundColor: "#F5F5F5",
justifyContent: "center",
alignItems: "center",
},
avatarPlaceholder: {
fontSize: 40,
},
username: {
fontSize: 20,
fontWeight: "bold",
marginBottom: 12,
},
editButton: {
backgroundColor: "#FF0000",
paddingVertical: 10,
paddingHorizontal: 40,
borderRadius: 8,
},
editButtonText: {
color: "#FFFFFF",
fontWeight: "bold",
},
tabContainer: {
flexDirection: "row",
justifyContent: "space-around",
paddingVertical: 12,
},
tab: {
paddingVertical: 8,
paddingHorizontal: 16,
},
activeTab: {
borderBottomWidth: 2,
borderBottomColor: "#333333",
},
tabText: {
fontWeight: "500",
},
divider: {
height: 1,
backgroundColor: "#EEEEEE",
marginHorizontal: 16,
},
foodGrid: {
flexDirection: "row",
flexWrap: "wrap",
padding: 8,
},
foodItem: {
width: "50%",
padding: 8,
position: "relative",
},
foodImage: {
width: "100%",
height: 120,
borderRadius: 8,
},
foodLabel: {
position: "absolute",
bottom: 16,
left: 16,
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 4,
},
foodLabelText: {
color: "#333333",
fontWeight: "bold",
fontSize: 12,
},
});

View File

@ -2,7 +2,6 @@ import { IconSymbol } from "@/components/ui/IconSymbol";
import { Image } from "expo-image"; import { Image } from "expo-image";
import { import {
ScrollView, ScrollView,
StyleSheet,
Text, Text,
TextInput, TextInput,
TouchableOpacity, TouchableOpacity,
@ -51,42 +50,47 @@ export default function RecipesScreen() {
]; ];
return ( return (
<SafeAreaView style={styles.container} edges={["top"]}> <SafeAreaView className="flex-1 bg-white" edges={["top"]}>
<ScrollView style={styles.scrollView}> <ScrollView className="flex-1">
{/* Search Bar */} {/* Search Bar */}
<View style={styles.searchContainer}> <View className="flex-row items-center mx-4 mt-2 mb-4 px-3 h-10 bg-white rounded-full border border-gray-300">
<IconSymbol name="magnifyingglass" size={20} color="#FF0000" /> <IconSymbol name="magnifyingglass" size={20} color="#FF0000" />
<TextInput <TextInput
style={styles.searchInput} className="flex-1 ml-2 text-[#333]"
placeholder="Search" placeholder="Search"
placeholderTextColor="#FF0000" placeholderTextColor="#FF0000"
/> />
</View> </View>
{/* Filter Buttons */} {/* Filter Buttons */}
<View style={styles.filterContainer}> <View className="flex-row mx-4 mb-4">
<TouchableOpacity style={styles.yellowButton}> <TouchableOpacity className="flex-1 bg-[#FFCC00] py-3 rounded-lg mr-2 items-center">
<Text style={styles.buttonText}>All Recipes</Text> <Text className="font-bold text-[#333]">All Recipes</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.redButton}> <TouchableOpacity className="flex-1 bg-red-600 py-3 rounded-lg items-center">
<Text style={styles.redButtonText}>My Recipes</Text> <Text className="font-bold text-white">My Recipes</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{/* Divider */} {/* Divider */}
<View style={styles.divider} /> <View className="h-px bg-[#EEEEEE] mx-4 mb-4" />
{/* Food Grid */} {/* Food Grid */}
<View style={styles.foodGrid}> <View className="flex-row flex-wrap p-2">
{foodItems.map((item) => ( {foodItems.map((item) => (
<View key={item.id} style={styles.foodItem}> <View key={item.id} className="w-1/2 p-2 relative">
<Image <Image
source={item.image} source={item.image}
style={styles.foodImage} className="w-full h-[120px] rounded-lg"
contentFit="cover" resizeMode="cover"
/> />
<View style={[styles.foodLabel, { backgroundColor: item.color }]}> <View
<Text style={styles.foodLabelText}>{item.name}</Text> className="absolute bottom-4 left-4 py-1 px-2 rounded bg-opacity-90"
style={{ backgroundColor: item.color }}
>
<Text className="text-[#333] font-bold text-xs">
{item.name}
</Text>
</View> </View>
</View> </View>
))} ))}
@ -95,93 +99,3 @@ export default function RecipesScreen() {
</SafeAreaView> </SafeAreaView>
); );
} }
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#FFFFFF",
},
scrollView: {
flex: 1,
},
searchContainer: {
flexDirection: "row",
alignItems: "center",
marginHorizontal: 16,
marginTop: 10,
marginBottom: 16,
paddingHorizontal: 12,
height: 40,
backgroundColor: "#FFFFFF",
borderRadius: 20,
borderWidth: 1,
borderColor: "#DDDDDD",
},
searchInput: {
flex: 1,
marginLeft: 8,
color: "#333333",
},
filterContainer: {
flexDirection: "row",
marginHorizontal: 16,
marginBottom: 16,
},
yellowButton: {
flex: 1,
backgroundColor: "#FFCC00",
paddingVertical: 12,
borderRadius: 8,
marginRight: 8,
alignItems: "center",
},
redButton: {
flex: 1,
backgroundColor: "#FF0000",
paddingVertical: 12,
borderRadius: 8,
alignItems: "center",
},
buttonText: {
fontWeight: "bold",
color: "#333333",
},
redButtonText: {
fontWeight: "bold",
color: "#FFFFFF",
},
divider: {
height: 1,
backgroundColor: "#EEEEEE",
marginHorizontal: 16,
marginBottom: 16,
},
foodGrid: {
flexDirection: "row",
flexWrap: "wrap",
padding: 8,
},
foodItem: {
width: "50%",
padding: 8,
position: "relative",
},
foodImage: {
width: "100%",
height: 120,
borderRadius: 8,
},
foodLabel: {
position: "absolute",
bottom: 16,
left: 16,
paddingVertical: 4,
paddingHorizontal: 8,
borderRadius: 4,
},
foodLabelText: {
color: "#333333",
fontWeight: "bold",
fontSize: 12,
},
});

View File

@ -1,30 +1,25 @@
import { DefaultTheme, ThemeProvider } from "@react-navigation/native";
import { useFonts } from "expo-font";
import { Stack } from "expo-router"; import { Stack } from "expo-router";
import { StatusBar } from "expo-status-bar"; import { GestureHandlerRootView } from "react-native-gesture-handler";
import "react-native-reanimated"; import "../global.css";
import { useColorScheme } from "@/hooks/useColorScheme";
export default function RootLayout() { export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
});
if (!loaded) {
// Async font loading only occurs in development.
return null;
}
return ( return (
// Use DarkTheme if need dark theme <GestureHandlerRootView style={{ flex: 1 }}>
<ThemeProvider value={colorScheme === "dark" ? DefaultTheme : DefaultTheme}> <Stack screenOptions={{ headerShown: false }}>
<Stack> <Stack.Screen
<Stack.Screen name="(tabs)" options={{ headerShown: false }} /> name="(tabs)"
<Stack.Screen name="+not-found" /> options={{
headerShown: false,
}}
/>
<Stack.Screen
name="recipe-detail"
options={{
headerShown: false,
presentation: "card",
}}
/>
</Stack> </Stack>
<StatusBar style="auto" /> </GestureHandlerRootView>
</ThemeProvider>
); );
} }

140
app/recipe-detail.tsx Normal file
View File

@ -0,0 +1,140 @@
import React from 'react';
import { View, Text, Image, TouchableOpacity, ScrollView, SafeAreaView, StatusBar } from 'react-native';
import { Feather, FontAwesome5 } from '@expo/vector-icons';
import { useLocalSearchParams, router } from 'expo-router';
export default function RecipeDetailScreen() {
const { title, image } = useLocalSearchParams();
const recipeTitle = title || "Pad Kra Pao Moo Sab with Eggs";
const recipeImage = typeof image === 'string' ? image : "/placeholder.svg?height=400&width=400&query=thai basil stir fry with egg and rice";
return (
<SafeAreaView className="flex-1 bg-white">
<StatusBar barStyle="dark-content" />
<ScrollView className="flex-1" showsVerticalScrollIndicator={false}>
{/* Header with back and share buttons */}
<View className="flex-row justify-between items-center px-4 pt-4 absolute z-10 w-full">
<TouchableOpacity
className="bg-[#ffd60a] p-3 rounded-lg"
onPress={() => router.back()}
>
<Feather name="arrow-left" size={24} color="#bb0718" />
</TouchableOpacity>
<TouchableOpacity className="bg-white p-3 rounded-lg">
<Feather name="send" size={24} color="#ffd60a" />
</TouchableOpacity>
</View>
{/* Recipe Image */}
<View className="items-center justify-center pt-16 pb-6">
<Image
source={{ uri: recipeImage }}
className="w-72 h-72 rounded-full"
/>
</View>
{/* Recipe Title and Description */}
<View className="px-6">
<Text className="text-4xl font-bold">{recipeTitle}</Text>
<Text className="text-gray-600 mt-2 text-lg">
Pad kra pao, also written as pad gaprao, is a popular Thai stir fry of ground meat and holy basil.
</Text>
{/* Recipe Info */}
<View className="flex-row justify-between mt-8">
<View>
<Text className="text-2xl font-bold">Skills</Text>
<Text className="text-gray-600 mt-1">Easy</Text>
</View>
<View>
<Text className="text-2xl font-bold">Time</Text>
<Text className="text-gray-600 mt-1">30 Mins</Text>
</View>
<View>
<Text className="text-2xl font-bold">Ingredients</Text>
<Text className="text-gray-600 mt-1">10+</Text>
</View>
<View>
<Text className="text-2xl font-bold">Calories</Text>
<Text className="text-gray-600 mt-1">300 kCal</Text>
</View>
</View>
{/* Ingredients */}
<Text className="text-3xl font-bold mt-12">Ingredients</Text>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
className="mt-6 mb-4"
contentContainerStyle={{ paddingLeft: 4, paddingRight: 20 }}
>
<View className="flex-row space-x-6">
<View className="w-24 h-24 bg-gray-300 rounded-lg" />
<View className="w-24 h-24 bg-gray-300 rounded-lg" />
<View className="w-24 h-24 bg-gray-300 rounded-lg" />
<View className="w-24 h-24 bg-gray-300 rounded-lg" />
<View className="w-24 h-24 bg-gray-300 rounded-lg" />
</View>
</ScrollView>
{/* Nutrition Info */}
<View className="bg-[#ffd60a] rounded-lg p-6 mt-10">
<View className="flex-row justify-between">
<View className="items-center">
<View className="w-16 h-16 rounded-full border-4 border-[#397e36] items-center justify-center">
<Text className="text-xl font-bold">0</Text>
<Text className="text-xs">/32g</Text>
</View>
<Text className="mt-2 font-semibold">Fat</Text>
</View>
<View className="items-center">
<View className="w-16 h-16 rounded-full border-4 border-[#397e36] items-center justify-center">
<Text className="text-xl font-bold">0</Text>
<Text className="text-xs">/32g</Text>
</View>
<Text className="mt-2 font-semibold">Fiber</Text>
</View>
<View className="items-center">
<View className="w-16 h-16 rounded-full border-4 border-[#a07d1a] items-center justify-center">
<Text className="text-xl font-bold">0</Text>
<Text className="text-xs">/32g</Text>
</View>
<Text className="mt-2 font-semibold">Protein</Text>
</View>
<View className="items-center">
<View className="w-16 h-16 rounded-full border-4 border-[#c87a20] items-center justify-center">
<Text className="text-xl font-bold">0</Text>
<Text className="text-xs">/32g</Text>
</View>
<Text className="mt-2 font-semibold">Carbs</Text>
</View>
</View>
</View>
</View>
{/* Bottom Spacing */}
<View className="h-28" />
</ScrollView>
{/* Cook Button */}
<View className="absolute bottom-0 left-0 right-0">
<View className="bg-[#bb0718] py-4 items-center">
<View className="flex-row items-center">
<Text className="text-[#ffd60a] text-2xl font-bold mr-2">Let's Cook!</Text>
<FontAwesome5 name="utensils" size={24} color="#ffd60a" />
</View>
</View>
<View className="bg-[#ffd60a] h-16" style={{ borderTopLeftRadius: 0, borderTopRightRadius: 0, borderBottomLeftRadius: 50, borderBottomRightRadius: 50 }} />
</View>
</SafeAreaView>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 650 KiB

9
babel.config.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
};
};

3
global.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

6
metro.config.js Normal file
View File

@ -0,0 +1,6 @@
const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require('nativewind/metro');
const config = getDefaultConfig(__dirname)
module.exports = withNativeWind(config, { input: './global.css' })

1
nativewind-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="nativewind/types" />

487
package-lock.json generated
View File

@ -19,6 +19,7 @@
"expo-font": "~13.3.1", "expo-font": "~13.3.1",
"expo-haptics": "~14.1.4", "expo-haptics": "~14.1.4",
"expo-image": "~2.1.7", "expo-image": "~2.1.7",
"expo-image-picker": "~16.1.4",
"expo-linking": "~7.1.4", "expo-linking": "~7.1.4",
"expo-router": "~5.0.6", "expo-router": "~5.0.6",
"expo-splash-screen": "~0.30.8", "expo-splash-screen": "~0.30.8",
@ -26,6 +27,7 @@
"expo-symbols": "~0.4.4", "expo-symbols": "~0.4.4",
"expo-system-ui": "~5.0.7", "expo-system-ui": "~5.0.7",
"expo-web-browser": "~14.1.6", "expo-web-browser": "~14.1.6",
"nativewind": "^4.1.23",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",
"react-native": "0.79.2", "react-native": "0.79.2",
@ -34,7 +36,8 @@
"react-native-safe-area-context": "5.4.0", "react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.10.0", "react-native-screens": "~4.10.0",
"react-native-web": "~0.20.0", "react-native-web": "~0.20.0",
"react-native-webview": "13.13.5" "react-native-webview": "13.13.5",
"tailwindcss": "^3.4.17"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",
@ -57,6 +60,18 @@
} }
} }
}, },
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@ -2249,6 +2264,7 @@
"version": "14.1.0", "version": "14.1.0",
"resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.1.0.tgz", "resolved": "https://registry.npmjs.org/@expo/vector-icons/-/vector-icons-14.1.0.tgz",
"integrity": "sha512-7T09UE9h8QDTsUeMGymB4i+iqvtEeaO5VvUjryFB4tugDTG/bkzViWA74hm5pfjjDEhYMXWaX112mcvhccmIwQ==", "integrity": "sha512-7T09UE9h8QDTsUeMGymB4i+iqvtEeaO5VvUjryFB4tugDTG/bkzViWA74hm5pfjjDEhYMXWaX112mcvhccmIwQ==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"expo-font": "*", "expo-font": "*",
"react": "*", "react": "*",
@ -2678,7 +2694,6 @@
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"dependencies": { "dependencies": {
"@nodelib/fs.stat": "2.0.5", "@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9" "run-parallel": "^1.1.9"
@ -2691,7 +2706,6 @@
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"engines": { "engines": {
"node": ">= 8" "node": ">= 8"
} }
@ -2700,7 +2714,6 @@
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"dependencies": { "dependencies": {
"@nodelib/fs.scandir": "2.1.5", "@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0" "fastq": "^1.6.0"
@ -4014,6 +4027,12 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/array-timsort": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz",
"integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==",
"license": "MIT"
},
"node_modules/array.prototype.findlast": { "node_modules/array.prototype.findlast": {
"version": "1.2.5", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
@ -4403,6 +4422,18 @@
"node": ">=0.6" "node": ">=0.6"
} }
}, },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
@ -4656,6 +4687,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001717", "version": "1.0.30001717",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
@ -4690,6 +4730,42 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/chownr": { "node_modules/chownr": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
@ -4875,6 +4951,22 @@
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/comment-json": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz",
"integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==",
"license": "MIT",
"dependencies": {
"array-timsort": "^1.0.3",
"core-util-is": "^1.0.3",
"esprima": "^4.0.1",
"has-own-prop": "^2.0.0",
"repeat-string": "^1.6.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/compressible": { "node_modules/compressible": {
"version": "2.0.18", "version": "2.0.18",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
@ -5012,6 +5104,12 @@
"url": "https://opencollective.com/core-js" "url": "https://opencollective.com/core-js"
} }
}, },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/cors": { "node_modules/cors": {
"version": "2.8.5", "version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
@ -5116,6 +5214,18 @@
"hyphenate-style-name": "^1.0.3" "hyphenate-style-name": "^1.0.3"
} }
}, },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@ -5333,6 +5443,18 @@
"node": ">=0.10" "node": ">=0.10"
} }
}, },
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"license": "Apache-2.0"
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"license": "MIT"
},
"node_modules/doctrine": { "node_modules/doctrine": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@ -6197,6 +6319,27 @@
} }
} }
}, },
"node_modules/expo-image-loader": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-5.1.0.tgz",
"integrity": "sha512-sEBx3zDQIODWbB5JwzE7ZL5FJD+DK3LVLWBVJy6VzsqIA6nDEnSFnsnWyCfCTSvbGigMATs1lgkC2nz3Jpve1Q==",
"license": "MIT",
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-image-picker": {
"version": "16.1.4",
"resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-16.1.4.tgz",
"integrity": "sha512-bTmmxtw1AohUT+HxEBn2vYwdeOrj1CLpMXKjvi9FKSoSbpcarT4xxI0z7YyGwDGHbrJqyyic3I9TTdP2J2b4YA==",
"license": "MIT",
"dependencies": {
"expo-image-loader": "~5.1.0"
},
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-keep-awake": { "node_modules/expo-keep-awake": {
"version": "14.1.4", "version": "14.1.4",
"resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz",
@ -6518,7 +6661,6 @@
"version": "3.3.3", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dev": true,
"dependencies": { "dependencies": {
"@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3", "@nodelib/fs.walk": "^1.2.3",
@ -6534,7 +6676,6 @@
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": { "dependencies": {
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
}, },
@ -6572,7 +6713,6 @@
"version": "1.19.1", "version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"dev": true,
"dependencies": { "dependencies": {
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
@ -7004,7 +7144,6 @@
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"dependencies": { "dependencies": {
"is-glob": "^4.0.3" "is-glob": "^4.0.3"
}, },
@ -7125,6 +7264,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/has-own-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz",
"integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/has-property-descriptors": { "node_modules/has-property-descriptors": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@ -7483,6 +7631,18 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-boolean-object": { "node_modules/is-boolean-object": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
@ -7605,7 +7765,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -7655,7 +7814,6 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": { "dependencies": {
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
}, },
@ -8092,6 +8250,15 @@
"resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz",
"integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==" "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww=="
}, },
"node_modules/jiti": {
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"license": "MIT",
"bin": {
"jiti": "bin/jiti.js"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -8456,6 +8623,18 @@
"url": "https://opencollective.com/parcel" "url": "https://opencollective.com/parcel"
} }
}, },
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/lines-and-columns": { "node_modules/lines-and-columns": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@ -8650,7 +8829,6 @@
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"engines": { "engines": {
"node": ">= 8" "node": ">= 8"
} }
@ -9134,6 +9312,23 @@
"url": "https://opencollective.com/napi-postinstall" "url": "https://opencollective.com/napi-postinstall"
} }
}, },
"node_modules/nativewind": {
"version": "4.1.23",
"resolved": "https://registry.npmjs.org/nativewind/-/nativewind-4.1.23.tgz",
"integrity": "sha512-oLX3suGI6ojQqWxdQezOSM5GmJ4KvMnMtmaSMN9Ggb5j7ysFt4nHxb1xs8RDjZR7BWc+bsetNJU8IQdQMHqRpg==",
"license": "MIT",
"dependencies": {
"comment-json": "^4.2.5",
"debug": "^4.3.7",
"react-native-css-interop": "0.1.22"
},
"engines": {
"node": ">=16"
},
"peerDependencies": {
"tailwindcss": ">3.3.0"
}
},
"node_modules/natural-compare": { "node_modules/natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -9258,6 +9453,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@ -9716,6 +9920,15 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/pirates": { "node_modules/pirates": {
"version": "4.0.7", "version": "4.0.7",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
@ -9790,6 +10003,127 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/postcss-import": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
"resolve": "^1.1.7"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"postcss": "^8.0.0"
}
},
"node_modules/postcss-js": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"license": "MIT",
"dependencies": {
"camelcase-css": "^2.0.1"
},
"engines": {
"node": "^12 || ^14 || >= 16"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
"peerDependencies": {
"postcss": "^8.4.21"
}
},
"node_modules/postcss-load-config": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"lilconfig": "^3.0.0",
"yaml": "^2.3.4"
},
"engines": {
"node": ">= 14"
},
"peerDependencies": {
"postcss": ">=8.0.9",
"ts-node": ">=9.0.0"
},
"peerDependenciesMeta": {
"postcss": {
"optional": true
},
"ts-node": {
"optional": true
}
}
},
"node_modules/postcss-load-config/node_modules/yaml": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/postcss-nested": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.1.1"
},
"engines": {
"node": ">=12.0"
},
"peerDependencies": {
"postcss": "^8.2.14"
}
},
"node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/postcss-value-parser": { "node_modules/postcss-value-parser": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
@ -9979,7 +10313,6 @@
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -10178,6 +10511,49 @@
} }
} }
}, },
"node_modules/react-native-css-interop": {
"version": "0.1.22",
"resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.1.22.tgz",
"integrity": "sha512-Mu01e+H9G+fxSWvwtgWlF5MJBJC4VszTCBXopIpeR171lbeBInHb8aHqoqRPxmJpi3xIHryzqKFOJYAdk7PBxg==",
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.22.15",
"@babel/traverse": "^7.23.0",
"@babel/types": "^7.23.0",
"debug": "^4.3.7",
"lightningcss": "^1.27.0",
"semver": "^7.6.3"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"react": ">=18",
"react-native": "*",
"react-native-reanimated": ">=3.6.2",
"tailwindcss": "~3"
},
"peerDependenciesMeta": {
"react-native-safe-area-context": {
"optional": true
},
"react-native-svg": {
"optional": true
}
}
},
"node_modules/react-native-css-interop/node_modules/semver": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/react-native-edge-to-edge": { "node_modules/react-native-edge-to-edge": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz", "resolved": "https://registry.npmjs.org/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz",
@ -10353,6 +10729,39 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"license": "MIT",
"dependencies": {
"pify": "^2.3.0"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/readdirp/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/reflect.getprototypeof": { "node_modules/reflect.getprototypeof": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@ -10459,6 +10868,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/repeat-string": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
"integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
"license": "MIT",
"engines": {
"node": ">=0.10"
}
},
"node_modules/require-directory": { "node_modules/require-directory": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -10582,7 +11000,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"dev": true,
"engines": { "engines": {
"iojs": ">=1.0.0", "iojs": ">=1.0.0",
"node": ">=0.10.0" "node": ">=0.10.0"
@ -10643,7 +11060,6 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -11547,6 +11963,43 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/tailwindcss": {
"version": "3.4.17",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.21.6",
"lilconfig": "^3.1.3",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.1.1",
"postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.2",
"postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.8",
"sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tar": { "node_modules/tar": {
"version": "7.4.3", "version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
@ -12130,6 +12583,12 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
} }
}, },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/utils-merge": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",

View File

@ -22,6 +22,7 @@
"expo-font": "~13.3.1", "expo-font": "~13.3.1",
"expo-haptics": "~14.1.4", "expo-haptics": "~14.1.4",
"expo-image": "~2.1.7", "expo-image": "~2.1.7",
"expo-image-picker": "~16.1.4",
"expo-linking": "~7.1.4", "expo-linking": "~7.1.4",
"expo-router": "~5.0.6", "expo-router": "~5.0.6",
"expo-splash-screen": "~0.30.8", "expo-splash-screen": "~0.30.8",
@ -29,6 +30,7 @@
"expo-symbols": "~0.4.4", "expo-symbols": "~0.4.4",
"expo-system-ui": "~5.0.7", "expo-system-ui": "~5.0.7",
"expo-web-browser": "~14.1.6", "expo-web-browser": "~14.1.6",
"nativewind": "^4.1.23",
"react": "19.0.0", "react": "19.0.0",
"react-dom": "19.0.0", "react-dom": "19.0.0",
"react-native": "0.79.2", "react-native": "0.79.2",
@ -37,7 +39,8 @@
"react-native-safe-area-context": "5.4.0", "react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.10.0", "react-native-screens": "~4.10.0",
"react-native-web": "~0.20.0", "react-native-web": "~0.20.0",
"react-native-webview": "13.13.5" "react-native-webview": "13.13.5",
"tailwindcss": "^3.4.17"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",

16
tailwind.config.js Normal file
View File

@ -0,0 +1,16 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
// NOTE: Update this to include the paths to all of your component files.
content: ["./app/**/*.{js,jsx,ts,tsx}"],
presets: [require("nativewind/preset")],
theme: {
extend: {
colors: {
primary: "#ffd60a",
secondary: "#f9be25",
accent: "#bb0718",
},
},
},
plugins: [],
}

View File

@ -12,6 +12,7 @@
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".expo/types/**/*.ts", ".expo/types/**/*.ts",
"expo-env.d.ts" "expo-env.d.ts",
"nativewind-env.d.ts"
] ]
} }