import { View, StyleSheet, FlatList, TextInput, Text, ActivityIndicator, Animated } from 'react-native'
import React, { useEffect } from 'react'
import { useState } from 'react'
import FoodItem from '@/components/FoodItem'
import { AntDesign } from '@expo/vector-icons'
import { useSQLiteContext } from 'expo-sqlite';
import { SearchFood } from '../../functions/SearchFood'
import { useAppTheme, subscribeToTheme } from '../../hooks/colorScheme';
export default function Track({ navigation }) {
const [colors, setColors] = useState(useAppTheme());
const [searchResults, setSearchResults] = useState([]);
const [noResults, setNoResults] = useState(false);
const [search, setSearch] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [showEmptySearchPrompt, setShowEmptySearchPrompt] = useState(false);
const fadeAnim = React.useRef(new Animated.Value(0)).current;
const searchCustomFoods = async (searchTerm) => {
try {
const results = await database.getAllAsync("SELECT * FROM customfoods");
const matchingResults = results.filter(item =>
item.name.toLowerCase().trim().includes(searchTerm.toLowerCase().trim())
);
return matchingResults;
} catch (error) {
console.error("Error searching custom foods:", error);
return [];
}
};
// Updated performSearch function
const performSearch = async (searchTerm) => {
// Reset states
setError(null);
// Handle empty search
if (!searchTerm?.trim()) {
setSearchResults([]);
setNoResults(false);
setShowEmptySearchPrompt(true);
// Trigger fade in
fadeAnim.setValue(0);
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
return;
}
setShowEmptySearchPrompt(false);
setIsLoading(true);
try {
const apiResults = await SearchFood(searchTerm);
const customResults = await searchCustomFoods(searchTerm);
const modifiedCustomResults = customResults.map(item => ({
...item,
id: `custom_${item.id}`
}));
const combinedResults = [...modifiedCustomResults, ...apiResults];
setSearchResults(combinedResults);
setNoResults(combinedResults.length === 0);
} catch (error) {
// attempt search again
try {
const apiResults = await SearchFood(searchTerm);
const customResults = await searchCustomFoods(searchTerm);
const modifiedCustomResults = customResults.map(item => ({
...item,
id: `custom_${item.id}`
}));
const combinedResults = [...modifiedCustomResults, ...apiResults];
setSearchResults(combinedResults);
setNoResults(combinedResults.length === 0);
} catch (error) {
console.error("second search failed:", error);
}
console.error("Search failed:", error);
setError("Failed to load results. Please try again.");
setSearchResults([]);
} finally {
setIsLoading(false);
}
};
const ItemSeparator = () => (
<View style={{ height: 5 }} />
);
const getItemLayout = (data, index) => ({
length: 80, // Approximate height of each item
offset: 80 * index,
index,
});
const database = useSQLiteContext();
useEffect(() => {
const unsubscribe = subscribeToTheme(newColors => {
setColors(newColors);
});
return () => unsubscribe();
}, []);
return (
<View style={[styles.container, {backgroundColor: colors.background}]}>
<View style={[styles.card, {backgroundColor: colors.boxes}]}>
<TextInput
value={search}
onChangeText={setSearch}
onSubmitEditing={() => performSearch(search)}
returnKeyType="search"
placeholder="Search for Foods..."
placeholderTextColor={colors.text + '80'}
style={[styles.input, {color: colors.text}]}
/>
</View>
<View style={[styles.buttonContainer, {backgroundColor: colors.background}]}>
<View style={[styles.buttonCard, {backgroundColor: colors.boxes}]}>
<AntDesign.Button
style={styles.button}
name="search1"
activeOpacity={0.5}
underlayColor={'transparent'}
onPress={() => performSearch(search)}
backgroundColor="transparent"
color={colors.accent}>
Search
</AntDesign.Button>
</View>
{false && <View style={[styles.buttonCard, {backgroundColor: colors.boxes}]}>
<AntDesign.Button
style={styles.button}
name="scan1"
activeOpacity={0.5}
underlayColor={'transparent'}
onPress={() => navigation.navigate('Scan')}
backgroundColor="transparent"
color={colors.accent}>
Scan
</AntDesign.Button>
</View>}
<View style={[styles.buttonCard, {backgroundColor: colors.boxes}]}>
<AntDesign.Button
style={styles.button}
name="addfile"
activeOpacity={0.5}
underlayColor={'transparent'}
onPress={() => navigation.navigate('QuickAdd')}
backgroundColor="transparent"
color={colors.accent}>
Quick Add
</AntDesign.Button>
</View>
</View>
{isLoading && (
<View style={styles.centerContent}>
<ActivityIndicator size="large" color={colors.accent} />
<Text style={[styles.messageText, {color: colors.text}]}>
Searching...
</Text>
</View>
)}
{error && (
<View style={styles.centerContent}>
<Text style={[styles.errorText, {color: colors.error || '#ff6b6b'}]}>
{error}
</Text>
</View>
)}
{showEmptySearchPrompt && !isLoading && !error && (
<Animated.Text style={[
styles.noResults,
{
color: colors.text,
opacity: fadeAnim
}
]}>
Please enter a food name to search
</Animated.Text>
)}
{noResults && !isLoading && !error && !showEmptySearchPrompt && (
<Text style={[styles.noResults, {color: colors.text}]}>
No results found...
</Text>
)}
{!isLoading && !error && (
<FlatList
data={searchResults} // Using state-managed results
keyExtractor={item => item.id ? item.id.toString() : Math.random().toString()}
renderItem={({item}) => <FoodItem item={item} />}
ItemSeparatorComponent={ItemSeparator}
getItemLayout={getItemLayout}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
showsVerticalScrollIndicator={false}
removeClippedSubviews={true}
contentContainerStyle={{paddingHorizontal: 5}}
/>
)}
</View>)
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 15,
gap: 16,
},
card: {
borderRadius: 20,
padding: 5,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 5,
marginTop: 65,
},
input: {
padding: 10,
fontSize: 16,
fontWeight: '500',
},
button: {
justifyContent: 'center',
minWidth: "45%",
height: 50,
paddingHorizontal: 15,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-evenly',
paddingHorizontal: 5,
},
buttonCard: {
borderRadius: 20,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 5,
},
noResults: {
fontSize: 16,
marginTop: 50,
textAlign: 'center',
},
centerContent: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
messageText: {
fontSize: 16,
marginTop: 10,
textAlign: 'center',
},
errorText: {
fontSize: 16,
textAlign: 'center',
marginBottom: 10,
},
})