import React from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Platform, Dimensions, Image, } from 'react-native'; import { BlurView as ExpoBlurView } from 'expo-blur'; import { MaterialIcons, Feather } from '@expo/vector-icons'; // Optional iOS Glass effect (expo-glass-effect) with safe fallback for FloatingHeader let GlassViewComp: any = null; let liquidGlassAvailable = false; if (Platform.OS === 'ios') { try { // Dynamically require so app still runs if the package isn't installed yet const glass = require('expo-glass-effect'); GlassViewComp = glass.GlassView; liquidGlassAvailable = typeof glass.isLiquidGlassAvailable === 'function' ? glass.isLiquidGlassAvailable() : false; } catch { GlassViewComp = null; liquidGlassAvailable = false; } } import Animated, { useAnimatedStyle, interpolate, Extrapolate, useAnimatedReaction, runOnJS, SharedValue, } from 'react-native-reanimated'; import { useTheme } from '../../contexts/ThemeContext'; import { logger } from '../../utils/logger'; const { width } = Dimensions.get('window'); interface FloatingHeaderProps { metadata: any; logoLoadError: boolean; handleBack: () => void; handleToggleLibrary: () => void; inLibrary: boolean; headerOpacity: SharedValue; headerElementsY: SharedValue; headerElementsOpacity: SharedValue; safeAreaTop: number; setLogoLoadError: (error: boolean) => void; stableLogoUri?: string | null; } const FloatingHeader: React.FC = ({ metadata, logoLoadError, handleBack, handleToggleLibrary, inLibrary, headerOpacity, headerElementsY, headerElementsOpacity, safeAreaTop, setLogoLoadError, stableLogoUri, }) => { const { currentTheme } = useTheme(); const [isHeaderInteractive, setIsHeaderInteractive] = React.useState(false); // Animated styles for the header const headerAnimatedStyle = useAnimatedStyle(() => ({ opacity: headerOpacity.value, transform: [ { translateY: interpolate(headerOpacity.value, [0, 1], [-20, 0], Extrapolate.CLAMP) } ] })); // Disable touches when header is transparent (Android can still register touches at opacity 0) useAnimatedReaction( () => headerOpacity.value, (opacity) => { const interactive = opacity > 0.05; runOnJS(setIsHeaderInteractive)(interactive); } ); // Animated style for header elements const headerElementsStyle = useAnimatedStyle(() => ({ opacity: headerElementsOpacity.value, transform: [{ translateY: headerElementsY.value }] })); return ( {Platform.OS === 'ios' ? ( {(stableLogoUri || metadata.logo) && !logoLoadError ? ( { logger.warn(`[FloatingHeader] Logo failed to load: ${stableLogoUri || metadata.logo}`); setLogoLoadError(true); }} /> ) : ( {metadata.name} )} ) : ( {metadata.logo && !logoLoadError ? ( { logger.warn(`[FloatingHeader] Logo failed to load: ${metadata.logo}`); setLogoLoadError(true); }} /> ) : ( {metadata.name} )} )} {Platform.OS === 'ios' && } ); }; const styles = StyleSheet.create({ floatingHeader: { position: 'absolute', top: 0, left: 0, right: 0, zIndex: 10, overflow: 'hidden', elevation: 4, // for Android shadow shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 3, }, blurContainer: { width: '100%', }, floatingHeaderContent: { height: 56, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, }, headerBottomBorder: { position: 'absolute', bottom: 0, left: 0, right: 0, height: 0.5, }, headerTitleContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 10, }, backButton: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center', borderRadius: 20, }, headerActionButton: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center', borderRadius: 20, }, floatingHeaderLogo: { height: 42, width: width * 0.6, maxWidth: 240, }, floatingHeaderTitle: { fontSize: 18, fontWeight: '700', textAlign: 'center', }, absoluteFill: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, }, }); export default React.memo(FloatingHeader);