← back to Beverlyhillsvideos App
components/FavoritesModal.tsx
207 lines
import React from 'react';
import {
Modal,
View,
Text,
TouchableOpacity,
FlatList,
StyleSheet,
SafeAreaView,
Linking,
} from 'react-native';
import { Colors } from '@/constants/Colors';
import { MapLocation } from '@/types';
interface Props {
visible: boolean;
onClose: () => void;
locations: MapLocation[];
favorites: Set<string>;
onToggleFavorite: (name: string) => void;
}
export default function FavoritesModal({
visible,
onClose,
locations,
favorites,
onToggleFavorite,
}: Props) {
const favorited = locations.filter((loc) => favorites.has(loc.name));
return (
<Modal
visible={visible}
animationType="slide"
presentationStyle="pageSheet"
onRequestClose={onClose}
>
<SafeAreaView style={styles.safeArea}>
<View style={styles.header}>
<Text style={styles.heading}>Favorites</Text>
<TouchableOpacity onPress={onClose} style={styles.doneBtn} hitSlop={8}>
<Text style={styles.doneText}>Done</Text>
</TouchableOpacity>
</View>
{favorited.length === 0 ? (
<View style={styles.empty}>
<Text style={styles.emptyIcon}>heart</Text>
<Text style={styles.emptyTitle}>No favorites yet</Text>
<Text style={styles.emptyBody}>
Tap the heart on any restaurant to save it here.
</Text>
</View>
) : (
<FlatList
data={favorited}
keyExtractor={(item) => item.name}
contentContainerStyle={styles.list}
ItemSeparatorComponent={() => <View style={styles.separator} />}
renderItem={({ item }) => (
<View style={styles.row}>
<View style={styles.rowInfo}>
<Text style={styles.rowName}>{item.name}</Text>
<Text style={styles.rowCategory}>{item.category}</Text>
<Text style={styles.rowAddress} numberOfLines={1}>
{item.address}
</Text>
</View>
<View style={styles.rowActions}>
{item.phone ? (
<TouchableOpacity
style={styles.actionBtn}
onPress={() => Linking.openURL(`tel:${item.phone}`)}
>
<Text style={styles.actionText}>Call</Text>
</TouchableOpacity>
) : null}
<TouchableOpacity
style={styles.heartBtn}
onPress={() => onToggleFavorite(item.name)}
hitSlop={6}
>
<Text style={styles.heart}>heart_filled</Text>
</TouchableOpacity>
</View>
</View>
)}
/>
)}
</SafeAreaView>
</Modal>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: Colors.cream,
},
header: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
paddingVertical: 14,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: Colors.border,
backgroundColor: Colors.cream,
},
heading: {
flex: 1,
fontSize: 20,
fontWeight: '700',
color: Colors.ink,
letterSpacing: 0.2,
},
doneBtn: {
paddingVertical: 4,
},
doneText: {
color: Colors.green,
fontSize: 16,
fontWeight: '600',
},
empty: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 40,
},
emptyIcon: {
fontSize: 40,
color: Colors.gold,
marginBottom: 12,
},
emptyTitle: {
fontSize: 20,
fontWeight: '600',
color: Colors.ink,
marginBottom: 8,
},
emptyBody: {
fontSize: 15,
color: Colors.textSecondary,
textAlign: 'center',
lineHeight: 22,
},
list: {
paddingVertical: 8,
},
separator: {
height: StyleSheet.hairlineWidth,
backgroundColor: Colors.border,
marginLeft: 20,
},
row: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
paddingVertical: 14,
backgroundColor: Colors.cream,
},
rowInfo: {
flex: 1,
marginRight: 12,
},
rowName: {
fontSize: 16,
fontWeight: '600',
color: Colors.ink,
marginBottom: 2,
},
rowCategory: {
fontSize: 13,
color: Colors.gold,
marginBottom: 2,
fontWeight: '500',
},
rowAddress: {
fontSize: 12,
color: Colors.textSecondary,
},
rowActions: {
flexDirection: 'row',
alignItems: 'center',
gap: 10,
},
actionBtn: {
backgroundColor: Colors.green,
paddingVertical: 6,
paddingHorizontal: 14,
borderRadius: 6,
},
actionText: {
color: '#fff',
fontSize: 13,
fontWeight: '600',
},
heartBtn: {
padding: 4,
},
heart: {
fontSize: 20,
color: Colors.gold,
},
});