A React Native app for the ultimate thinking partner.
at main 124 lines 3.2 kB view raw
1/** 2 * AppHeader Component 3 * 4 * Application header with menu button and app title. 5 * 6 * Features: 7 * - Menu button triggers sidebar drawer 8 * - Hidden developer mode toggle (tap "co" title 7 times in 2 seconds) 9 * - Responsive to safe area insets 10 * - Conditionally hides when no messages present 11 */ 12 13import React, { useRef, useState } from 'react'; 14import { View, Text, TouchableOpacity, StyleSheet, Platform, Alert } from 'react-native'; 15import { Ionicons } from '@expo/vector-icons'; 16import { useSafeAreaInsets } from 'react-native-safe-area-context'; 17import type { Theme } from '../theme'; 18 19interface AppHeaderProps { 20 theme: Theme; 21 colorScheme: 'light' | 'dark'; 22 hasMessages: boolean; 23 onMenuPress: () => void; 24 developerMode: boolean; 25 onDeveloperModeToggle: () => void; 26} 27 28export function AppHeader({ 29 theme, 30 colorScheme, 31 hasMessages, 32 onMenuPress, 33 developerMode, 34 onDeveloperModeToggle, 35}: AppHeaderProps) { 36 const insets = useSafeAreaInsets(); 37 const [headerClickCount, setHeaderClickCount] = useState(0); 38 const headerClickTimeoutRef = useRef<NodeJS.Timeout | null>(null); 39 40 const handleTitlePress = () => { 41 setHeaderClickCount(prev => prev + 1); 42 43 if (headerClickTimeoutRef.current) { 44 clearTimeout(headerClickTimeoutRef.current); 45 } 46 47 headerClickTimeoutRef.current = setTimeout(() => { 48 if (headerClickCount >= 6) { 49 onDeveloperModeToggle(); 50 if (Platform.OS === 'web') { 51 window.alert(developerMode ? 'Developer mode disabled' : 'Developer mode enabled'); 52 } else { 53 Alert.alert('Developer Mode', developerMode ? 'Disabled' : 'Enabled'); 54 } 55 } 56 setHeaderClickCount(0); 57 }, 2000); 58 }; 59 60 return ( 61 <View 62 style={[ 63 styles.header, 64 { 65 paddingTop: insets.top + 6, 66 backgroundColor: theme.colors.background.secondary, 67 borderBottomColor: theme.colors.border.primary, 68 }, 69 !hasMessages && { 70 backgroundColor: 'transparent', 71 borderBottomWidth: 0, 72 }, 73 ]} 74 > 75 <TouchableOpacity onPress={onMenuPress} style={styles.menuButton}> 76 <Ionicons 77 name="menu" 78 size={22} 79 color={colorScheme === 'dark' ? '#FFFFFF' : theme.colors.text.primary} 80 /> 81 </TouchableOpacity> 82 83 {hasMessages && ( 84 <> 85 <View style={styles.headerCenter}> 86 <TouchableOpacity onPress={handleTitlePress}> 87 <Text style={[styles.headerTitle, { color: theme.colors.text.primary }]}> 88 co 89 </Text> 90 </TouchableOpacity> 91 </View> 92 93 <View style={styles.headerSpacer} /> 94 </> 95 )} 96 </View> 97 ); 98} 99 100const styles = StyleSheet.create({ 101 header: { 102 flexDirection: 'row', 103 alignItems: 'center', 104 paddingHorizontal: 16, 105 paddingBottom: 6, 106 borderBottomWidth: 1, 107 }, 108 menuButton: { 109 padding: 6, 110 }, 111 headerCenter: { 112 flex: 1, 113 alignItems: 'center', 114 }, 115 headerTitle: { 116 fontSize: 36, 117 fontFamily: 'Lexend_700Bold', 118 }, 119 headerSpacer: { 120 width: 34, // Balance the menu button width 121 }, 122}); 123 124export default AppHeader;