|
| 1 | +import { useState, useEffect, memo } from 'react'; |
| 2 | +import { View, Text, TextInput, Pressable, FlatList, StyleSheet } from 'react-native'; |
| 3 | +import { StatusBar } from 'expo-status-bar'; |
| 4 | + |
| 5 | +function LiveClock() { |
| 6 | + const [now, setNow] = useState(Date.now()); |
| 7 | + useEffect(() => { |
| 8 | + const id = setInterval(() => setNow(Date.now()), 1000); |
| 9 | + return () => clearInterval(id); |
| 10 | + }, []); |
| 11 | + |
| 12 | + return ( |
| 13 | + <View style={styles.row}> |
| 14 | + <ClockDisplay timestamp={now} /> |
| 15 | + <ClockLabel /> |
| 16 | + </View> |
| 17 | + ); |
| 18 | +} |
| 19 | + |
| 20 | +function ClockDisplay({ timestamp }: { timestamp: number }) { |
| 21 | + return <Text style={styles.mono}>{new Date(timestamp).toLocaleTimeString()}</Text>; |
| 22 | +} |
| 23 | + |
| 24 | +function ClockLabel() { |
| 25 | + return <Text style={styles.muted}> local time</Text>; |
| 26 | +} |
| 27 | + |
| 28 | +const ITEMS = Array.from({ length: 50 }, (_, i) => ({ |
| 29 | + id: String(i), |
| 30 | + name: `Item ${i}`, |
| 31 | + category: ['A', 'B', 'C'][i % 3], |
| 32 | +})); |
| 33 | + |
| 34 | +const ListItem = memo(function ListItem({ name, category }: { name: string; category: string }) { |
| 35 | + return ( |
| 36 | + <View style={styles.listItem}> |
| 37 | + <Text>{name}</Text> |
| 38 | + <Text style={styles.muted}> ({category})</Text> |
| 39 | + </View> |
| 40 | + ); |
| 41 | +}); |
| 42 | + |
| 43 | +function ItemList({ filter }: { filter: string }) { |
| 44 | + const filtered = ITEMS.filter((item) => |
| 45 | + item.name.toLowerCase().includes(filter.toLowerCase()), |
| 46 | + ); |
| 47 | + |
| 48 | + return ( |
| 49 | + <FlatList |
| 50 | + data={filtered} |
| 51 | + keyExtractor={(item) => item.id} |
| 52 | + renderItem={({ item }) => <ListItem name={item.name} category={item.category} />} |
| 53 | + style={styles.list} |
| 54 | + /> |
| 55 | + ); |
| 56 | +} |
| 57 | + |
| 58 | +function NotificationBanner() { |
| 59 | + const [count, setCount] = useState(0); |
| 60 | + const [label, setLabel] = useState('No notifications'); |
| 61 | + |
| 62 | + useEffect(() => { |
| 63 | + setLabel(count === 0 ? 'No notifications' : `${count} notification${count > 1 ? 's' : ''}`); |
| 64 | + }, [count]); |
| 65 | + |
| 66 | + return ( |
| 67 | + <View style={[styles.banner, count > 0 ? styles.bannerWarning : styles.bannerSuccess]}> |
| 68 | + <Text>{label}</Text> |
| 69 | + <Pressable onPress={() => setCount((n) => n + 1)} style={styles.button}> |
| 70 | + <Text>Add</Text> |
| 71 | + </Pressable> |
| 72 | + <Pressable onPress={() => setCount(0)} style={styles.button}> |
| 73 | + <Text>Clear</Text> |
| 74 | + </Pressable> |
| 75 | + </View> |
| 76 | + ); |
| 77 | +} |
| 78 | + |
| 79 | +export default function HomeScreen() { |
| 80 | + const [filter, setFilter] = useState(''); |
| 81 | + |
| 82 | + return ( |
| 83 | + <View style={styles.container}> |
| 84 | + <StatusBar style="auto" /> |
| 85 | + <Text style={styles.title}>Perf Debug App</Text> |
| 86 | + <LiveClock /> |
| 87 | + <NotificationBanner /> |
| 88 | + <TextInput |
| 89 | + placeholder="Filter items..." |
| 90 | + value={filter} |
| 91 | + onChangeText={setFilter} |
| 92 | + style={styles.input} |
| 93 | + /> |
| 94 | + <ItemList filter={filter} /> |
| 95 | + </View> |
| 96 | + ); |
| 97 | +} |
| 98 | + |
| 99 | +const styles = StyleSheet.create({ |
| 100 | + container: { flex: 1, padding: 20, paddingTop: 60, backgroundColor: '#fff' }, |
| 101 | + title: { fontSize: 24, fontWeight: 'bold', marginBottom: 12 }, |
| 102 | + row: { flexDirection: 'row', alignItems: 'center', marginVertical: 4 }, |
| 103 | + mono: { fontFamily: 'monospace' }, |
| 104 | + muted: { color: '#888' }, |
| 105 | + banner: { flexDirection: 'row', alignItems: 'center', padding: 8, borderRadius: 4, marginVertical: 8 }, |
| 106 | + bannerWarning: { backgroundColor: '#ffeeba' }, |
| 107 | + bannerSuccess: { backgroundColor: '#d4edda' }, |
| 108 | + button: { marginLeft: 8, paddingHorizontal: 12, paddingVertical: 4, backgroundColor: '#e0e0e0', borderRadius: 4 }, |
| 109 | + input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 4, padding: 8, marginVertical: 8 }, |
| 110 | + list: { maxHeight: 300 }, |
| 111 | + listItem: { flexDirection: 'row', paddingVertical: 4 }, |
| 112 | +}); |
0 commit comments