import { BasalMetabolismCard } from '@/components/BasalMetabolismCard'; import { DateSelector } from '@/components/DateSelector'; import { FitnessRingsCard } from '@/components/FitnessRingsCard'; import { MenstrualCycleCard } from '@/components/MenstrualCycleCard'; import { MoodCard } from '@/components/MoodCard'; import { NutritionRadarCard } from '@/components/NutritionRadarCard'; import CircumferenceCard from '@/components/statistic/CircumferenceCard'; import OxygenSaturationCard from '@/components/statistic/OxygenSaturationCard'; import SleepCard from '@/components/statistic/SleepCard'; import SunlightCard from '@/components/statistic/SunlightCard'; import WristTemperatureCard from '@/components/statistic/WristTemperatureCard'; import StepsCard from '@/components/StepsCard'; import { StressMeter } from '@/components/StressMeter'; import WaterIntakeCard from '@/components/WaterIntakeCard'; import { WeightHistoryCard } from '@/components/weight/WeightHistoryCard'; import { WorkoutSummaryCard } from '@/components/WorkoutSummaryCard'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useAuthGuard } from '@/hooks/useAuthGuard'; import { syncDailyHealthReport, syncHealthKitToServer } from '@/services/healthKitSync'; import { setHealthData } from '@/store/healthSlice'; import { fetchDailyMoodCheckins, selectLatestMoodRecordByDate } from '@/store/moodSlice'; import { updateUserProfile } from '@/store/userSlice'; import { fetchTodayWaterStats } from '@/store/waterSlice'; import { getMonthDaysZh, getTodayIndexInMonth } from '@/utils/date'; import { fetchHealthDataForDate } from '@/utils/health'; import { logger } from '@/utils/logger'; import { DEFAULT_CARD_ORDER, getStatisticsCardOrder, getStatisticsCardsVisibility, StatisticsCardsVisibility } from '@/utils/userPreferences'; import { Ionicons } from '@expo/vector-icons'; import dayjs from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { LinearGradient } from 'expo-linear-gradient'; import { useFocusEffect, useRouter } from 'expo-router'; import { debounce } from 'lodash'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { AppState, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; // 浮动动画组件 const FloatingCard = ({ children, style }: { children: React.ReactNode; style?: any; }) => { return ( {children} ); }; export default function ExploreScreen() { const { t } = useTranslation(); const stepGoal = useAppSelector((s) => s.user.profile?.dailyStepsGoal) ?? 2000; const userProfile = useAppSelector((s) => s.user.profile); const todayWaterStats = useAppSelector((s) => s.water.todayStats); const { pushIfAuthedElseLogin, isLoggedIn, ensureLoggedIn } = useAuthGuard(); const router = useRouter(); // 使用 dayjs:当月日期与默认选中"今天" const [selectedIndex, setSelectedIndex] = useState(getTodayIndexInMonth()); // const tabBarHeight = useBottomTabBarHeight(); const insets = useSafeAreaInsets(); // 获取当前选中日期 - 使用 useMemo 缓存避免重复计算 const currentSelectedDate = useMemo(() => { const days = getMonthDaysZh(); return days[selectedIndex]?.date?.toDate() ?? new Date(); }, [selectedIndex]); const currentSelectedDateString = useMemo(() => { return dayjs(currentSelectedDate).format('YYYY-MM-DD'); }, [currentSelectedDate]); const handleOpenGallery = React.useCallback(async () => { const ok = await ensureLoggedIn(); if (!ok) return; router.push('/gallery'); }, [ensureLoggedIn, router]); const handleOpenCustomization = React.useCallback(() => { router.push('/statistics-customization'); }, [router]); // 用于触发动画重置的 token(当日期或数据变化时更新) const [animToken, setAnimToken] = useState(0); // 首页卡片显示设置 const [cardVisibility, setCardVisibility] = useState({ showMood: true, showSteps: true, showStress: true, showSleep: true, showFitnessRings: true, showWater: true, showBasalMetabolism: true, showOxygenSaturation: true, showWristTemperature: true, showMenstrualCycle: true, showWeight: true, showCircumference: true, showSunlight: true, }); const [cardOrder, setCardOrder] = useState(DEFAULT_CARD_ORDER); // 加载卡片设置 const loadSettings = useCallback(async () => { try { const [visibility, order] = await Promise.all([ getStatisticsCardsVisibility(), getStatisticsCardOrder(), ]); setCardVisibility(visibility); setCardOrder(order); } catch (error) { console.error('Failed to load card settings:', error); } }, []); // 页面聚焦时加载设置 useFocusEffect( useCallback(() => { loadSettings(); }, [loadSettings]) ); // 心情相关状态 const dispatch = useAppDispatch(); const [isMoodLoading, setIsMoodLoading] = useState(false); // 记录最近一次请求的"日期键",避免旧请求覆盖新结果 const latestRequestKeyRef = useRef(null); // 请求状态管理,防止重复请求 const loadingRef = useRef({ health: false, mood: false }); // 数据缓存时间戳,避免短时间内重复拉取 const dataTimestampRef = useRef<{ [key: string]: number }>({}); const getDateKey = (d: Date) => `${dayjs(d).year()}-${dayjs(d).month() + 1}-${dayjs(d).date()}`; // 检查数据是否需要刷新(5分钟内不重复拉取) const shouldRefreshData = (dateKey: string, dataType: string) => { const cacheKey = `${dateKey}-${dataType}`; const lastUpdate = dataTimestampRef.current[cacheKey]; const now = Date.now(); // 使用5分钟缓存时间 const cacheTime = 5 * 60 * 1000; return !lastUpdate || (now - lastUpdate) > cacheTime; }; // 更新数据时间戳 const updateDataTimestamp = (dateKey: string, dataType: string) => { const cacheKey = `${dateKey}-${dataType}`; dataTimestampRef.current[cacheKey] = Date.now(); }; // 从 Redux 获取当前日期的心情记录 const currentMoodCheckin = useAppSelector(selectLatestMoodRecordByDate( currentSelectedDateString )); // 加载心情数据 const loadMoodData = async (targetDate?: Date, forceRefresh = false) => { if (!isLoggedIn) return; // 确定要查询的日期 let derivedDate: Date; if (targetDate) { derivedDate = targetDate; } else { derivedDate = currentSelectedDate; } const requestKey = getDateKey(derivedDate); // 检查是否正在加载或不需要刷新 if (loadingRef.current.mood) { console.log('心情数据正在加载中,跳过重复请求'); return; } if (!forceRefresh && !shouldRefreshData(requestKey, 'mood')) { console.log('心情数据缓存未过期,跳过请求'); return; } try { loadingRef.current.mood = true; setIsMoodLoading(true); const dateString = dayjs(derivedDate).format('YYYY-MM-DD'); await dispatch(fetchDailyMoodCheckins(dateString)); // 更新缓存时间戳 updateDataTimestamp(requestKey, 'mood'); } catch (error) { console.error('加载心情数据失败:', error); } finally { loadingRef.current.mood = false; setIsMoodLoading(false); } }; const loadHealthData = async (targetDate?: Date, forceRefresh = false) => { // 确定要查询的日期 let derivedDate: Date; if (targetDate) { derivedDate = targetDate; } else { derivedDate = currentSelectedDate; } const requestKey = getDateKey(derivedDate); // 检查是否正在加载或不需要刷新 if (loadingRef.current.health) { console.log('健康数据正在加载中,跳过重复请求'); return; } if (!forceRefresh && !shouldRefreshData(requestKey, 'health')) { console.log('健康数据缓存未过期,跳过请求'); return; } try { loadingRef.current.health = true; console.log('=== 开始HealthKit初始化流程 ==='); latestRequestKeyRef.current = requestKey; console.log('权限获取成功,开始获取健康数据...', derivedDate); const data = await fetchHealthDataForDate(derivedDate); console.log('设置UI状态:', data); // 仅当该请求仍是最新时,才应用结果 if (latestRequestKeyRef.current === requestKey) { const dateString = dayjs(derivedDate).format('YYYY-MM-DD'); // 使用 Redux 存储健康数据 dispatch(setHealthData({ date: dateString, data: { activeCalories: data.activeEnergyBurned, heartRate: data.heartRate, activeEnergyBurned: data.activeEnergyBurned, activeCaloriesGoal: data.activeCaloriesGoal, exerciseMinutes: data.exerciseMinutes, exerciseMinutesGoal: data.exerciseMinutesGoal, standHours: data.standHours, standHoursGoal: data.standHoursGoal, } })); setAnimToken((t) => t + 1); // 更新缓存时间戳 updateDataTimestamp(requestKey, 'health'); } else { console.log('忽略过期健康数据请求结果,key=', requestKey, '最新key=', latestRequestKeyRef.current); } console.log('=== HealthKit数据获取完成 ==='); } catch (error) { console.error('HealthKit流程出现异常:', error); } finally { loadingRef.current.health = false; } }; // 加载营养数据 // 实际执行数据加载的方法 const executeLoadAllData = React.useCallback((targetDate?: Date, forceRefresh = false) => { const dateToUse = targetDate || currentSelectedDate; if (dateToUse) { console.log('执行数据加载,日期:', dateToUse, '强制刷新:', forceRefresh); loadHealthData(dateToUse, forceRefresh); if (isLoggedIn) { loadMoodData(dateToUse, forceRefresh); // 加载喝水数据(只加载今日数据用于后台检查) const isToday = dayjs(dateToUse).isSame(dayjs(), 'day'); if (isToday) { dispatch(fetchTodayWaterStats()); } } } }, [isLoggedIn, dispatch]); // 使用 lodash debounce 防抖的加载所有数据方法 const debouncedLoadAllData = React.useMemo( () => debounce(executeLoadAllData, 500), // 500ms 防抖延迟 [executeLoadAllData] ); // 对外暴露的 loadAllData 方法 const loadAllData = React.useCallback((targetDate?: Date, forceRefresh = false) => { if (forceRefresh) { // 如果是强制刷新,立即执行,不使用防抖 executeLoadAllData(targetDate, forceRefresh); } else { // 普通调用使用防抖 debouncedLoadAllData(targetDate, forceRefresh); } }, [executeLoadAllData, debouncedLoadAllData]); // 同步 HealthKit 数据到服务端(带智能 diff 比较) const syncHealthDataToServer = React.useCallback(async () => { if (!isLoggedIn || !userProfile) { logger.info('用户未登录,跳过 HealthKit 数据同步'); return; } try { logger.info('开始同步 HealthKit 个人健康数据到服务端...'); // 1. 同步个人资料 (身高、体重、出生日期) // 传入当前用户资料,用于 diff 比较 const success = await syncHealthKitToServer( async (data) => { await dispatch(updateUserProfile(data) as any); }, userProfile // 传入当前用户资料进行比较 ); if (success) { logger.info('HealthKit 个人资料同步到服务端成功'); } else { logger.info('HealthKit 个人资料同步到服务端跳过(无变化)或失败'); } // 2. 同步每日健康数据报表 (活动、睡眠、心率等) // 传入今日饮水量 const waterIntake = todayWaterStats?.totalAmount; logger.info('开始同步每日健康数据报表...', { waterIntake }); const reportSuccess = await syncDailyHealthReport(waterIntake); if (reportSuccess) { logger.info('每日健康数据报表同步成功'); } else { logger.info('每日健康数据报表同步跳过(无变化)或失败'); } } catch (error) { logger.error('同步 HealthKit 数据到服务端失败:', error); } }, [isLoggedIn, dispatch, userProfile, todayWaterStats]); // 初始加载时执行数据加载和同步 useEffect(() => { loadAllData(currentSelectedDate); // 延迟1秒后执行同步,避免影响初始加载性能 // 如果 todayWaterStats 还未加载完成,可能会导致第一次同步时 waterIntake 为 undefined // 但 waterSlice.fetchTodayWaterStats 会在 loadAllData 中被调用 const syncTimer = setTimeout(() => { syncHealthDataToServer(); }, 1000); return () => clearTimeout(syncTimer); }, []) // AppState 监听:应用从后台返回前台时的处理 useEffect(() => { const handleAppStateChange = (nextAppState: string) => { if (nextAppState === 'active') { // 判断当前选中的日期是否是最新的(今天) const todayIndex = getTodayIndexInMonth(); const isTodaySelected = selectedIndex === todayIndex; if (!isTodaySelected) { // 如果当前不是选中今天,则切换到今天(这个更新会触发数据加载) console.log('应用回到前台,切换到今天并加载数据'); setSelectedIndex(todayIndex); // 注意:这里不直接调用loadAllData,因为setSelectedIndex会触发useEffect重新计算currentSelectedDate // 然后onSelectDate会被调用,从而触发数据加载 } else { // 如果已经是今天,则直接调用加载数据的方法 console.log('应用回到前台,当前已是今天,直接加载数据'); loadAllData(currentSelectedDate); } } }; const subscription = AppState.addEventListener('change', handleAppStateChange); return () => { subscription?.remove(); }; }, [loadAllData, currentSelectedDate, selectedIndex]); // 日期点击时,加载对应日期数据 const onSelectDate = React.useCallback((index: number, date: Date) => { setSelectedIndex(index); console.log('日期切换,加载数据...', date); // 日期切换时不强制刷新,依赖缓存机制减少不必要的请求 // loadAllData 内部已经实现了防抖,无需额外防抖处理 loadAllData(date, false); }, [loadAllData]); return ( {/* 背景渐变 */} {/* 装饰性圆圈 */} {/* 顶部信息栏 */} {/* 右边文字区域 */} {t('statistics.title')} {isLiquidGlassAvailable() ? ( ) : ( )} {isLiquidGlassAvailable() ? ( ) : ( )} {/* 日期选择器 */} {/* 营养摄入雷达图卡片 */} {/* 身体指标section标题 */} {t('statistics.sections.bodyMetrics')} {/* 动态布局:支持混合瀑布流和全宽卡片 */} {(() => { // 定义所有卡片及其显示状态 const allCardsMap: Record = { mood: { visible: cardVisibility.showMood, component: ( pushIfAuthedElseLogin('/mood/calendar')} isLoading={isMoodLoading} /> ) }, steps: { visible: cardVisibility.showSteps, component: ( ) }, stress: { visible: cardVisibility.showStress, component: ( ) }, sleep: { visible: cardVisibility.showSleep, component: ( ) }, sunlight: { visible: cardVisibility.showSunlight, component: ( ) }, fitness: { visible: cardVisibility.showFitnessRings, component: ( ) }, water: { visible: cardVisibility.showWater, component: ( ) }, metabolism: { visible: cardVisibility.showBasalMetabolism, component: ( ) }, oxygen: { visible: cardVisibility.showOxygenSaturation, component: ( ) }, temperature: { visible: cardVisibility.showWristTemperature, component: ( ) }, menstrual: { visible: cardVisibility.showMenstrualCycle, component: ( pushIfAuthedElseLogin('/menstrual-cycle')} /> ) }, weight: { visible: cardVisibility.showWeight, isFullWidth: true, component: ( ) }, circumference: { visible: cardVisibility.showCircumference, isFullWidth: true, component: ( ) } }; const allKeys = Object.keys(allCardsMap); const sortedKeys = Array.from(new Set([...cardOrder, ...allKeys])) .filter(key => allCardsMap[key]); const visibleCards = sortedKeys .map(key => ({ id: key, ...allCardsMap[key] })) .filter(card => card.visible); // 分组逻辑:将连续的瀑布流卡片聚合,全宽卡片单独作为一组 const blocks: any[] = []; let currentMasonryBlock: any[] = []; visibleCards.forEach(card => { if (card.isFullWidth) { // 如果有未处理的瀑布流卡片,先结算 if (currentMasonryBlock.length > 0) { blocks.push({ type: 'masonry', items: [...currentMasonryBlock] }); currentMasonryBlock = []; } // 添加全宽卡片 blocks.push({ type: 'full', item: card }); } else { // 添加到当前瀑布流组 currentMasonryBlock.push(card); } }); // 结算剩余的瀑布流卡片 if (currentMasonryBlock.length > 0) { blocks.push({ type: 'masonry', items: [...currentMasonryBlock] }); } return blocks.map((block, blockIndex) => { if (block.type === 'full') { return ( {block.item.component} ); } else { // 渲染瀑布流块 const leftColumnCards = block.items.filter((_: any, index: number) => index % 2 === 0); const rightColumnCards = block.items.filter((_: any, index: number) => index % 2 !== 0); return ( {leftColumnCards.map((card: any) => ( {card.component} ))} {rightColumnCards.map((card: any) => ( {card.component} ))} ); } }); })()} ); } const primary = Colors.light.primary; const styles = StyleSheet.create({ container: { flex: 1, }, gradientBackground: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, }, decorativeCircle1: { position: 'absolute', top: 40, right: 20, width: 60, height: 60, borderRadius: 30, backgroundColor: '#0EA5E9', opacity: 0.1, }, decorativeCircle2: { position: 'absolute', bottom: -15, left: -15, width: 40, height: 40, borderRadius: 20, backgroundColor: '#0EA5E9', opacity: 0.05, }, scrollView: { flex: 1, }, headerContainer: { marginBottom: 10, }, headerContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 12, }, headerLeft: { flexDirection: 'row', alignItems: 'center', flex: 1, minWidth: 0, }, logoImage: { width: 28, height: 28, borderRadius: 20, }, headerTextContainer: { flex: 1, marginLeft: 12, }, headerTitle: { fontSize: 16, fontWeight: '700', color: '#192126', fontFamily: 'AliBold' }, debugButtonsContainer: { flexDirection: 'row', gap: 8, }, debugButton: { width: 32, height: 32, borderRadius: 16, backgroundColor: '#FF6B6B', alignItems: 'center', justifyContent: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, }, debugButtonText: { fontSize: 12, fontFamily: 'AliRegular', }, metricsRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16, }, card: { backgroundColor: '#0F1418', borderRadius: 22, padding: 18, marginBottom: 16, }, metricsLeft: { flex: 1, backgroundColor: '#EEE9FF', borderRadius: 22, padding: 18, marginRight: 12, }, metricsRight: { width: 160, gap: 12, }, metricsRightCard: { backgroundColor: '#FFFFFF', borderRadius: 22, padding: 16, }, caloriesValue: { color: '#192126', fontSize: 18, lineHeight: 18, fontWeight: '600', textAlignVertical: 'bottom', fontFamily: 'AliBold' }, caloriesUnit: { color: '#515558ff', fontSize: 12, marginLeft: 4, lineHeight: 18, fontFamily: 'AliRegular', }, trainingContent: { marginTop: 8, width: 120, height: 120, borderRadius: 60, alignItems: 'center', justifyContent: 'center', alignSelf: 'center', }, trainingRingTrack: { position: 'absolute', width: '100%', height: '100%', borderRadius: 60, borderWidth: 12, borderColor: '#E2D9FD', }, trainingRingProgress: { position: 'absolute', width: '100%', height: '100%', borderRadius: 60, borderWidth: 12, borderColor: 'transparent', borderTopColor: '#8B74F3', borderRightColor: '#8B74F3', transform: [{ rotateZ: '45deg' }], }, trainingPercent: { fontSize: 18, fontWeight: '800', color: '#8B74F3', fontFamily: 'AliBold', }, cyclingHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 12, }, cyclingIconBadge: { width: 30, height: 30, borderRadius: 6, backgroundColor: primary, alignItems: 'center', justifyContent: 'center', marginRight: 8, }, cyclingTitle: { color: '#FFFFFF', fontSize: 20, fontWeight: '800', fontFamily: 'AliBold', }, mapArea: { backgroundColor: 'rgba(255,255,255,0.08)', borderRadius: 14, height: 180, padding: 8, flexDirection: 'row', flexWrap: 'wrap', overflow: 'hidden', }, mapTile: { width: '25%', height: '25%', borderWidth: 1, borderColor: 'rgba(255,255,255,0.12)', }, routeLine: { position: 'absolute', height: 6, backgroundColor: primary, borderRadius: 3, }, cardHeaderRow: { flexDirection: 'row', alignItems: 'center', marginBottom: 12, }, iconSquare: { width: 24, height: 24, borderRadius: 8, backgroundColor: '#FFFFFF', alignItems: 'center', justifyContent: 'center', marginRight: 10, }, cardTitle: { fontSize: 14, color: '#192126', fontFamily: 'AliBold', }, heartCard: { backgroundColor: '#FFE5E5', }, waveContainer: { flexDirection: 'row', alignItems: 'flex-end', height: 70, gap: 6, marginBottom: 8, }, waveBar: { width: 6, borderRadius: 3, backgroundColor: '#E54D4D', }, heartValue: { alignSelf: 'flex-end', color: '#5B5B5B', fontWeight: '600', fontFamily: 'AliBold', }, stepsValue: { fontSize: 14, color: '#7A6A42', fontWeight: '700', marginBottom: 8, fontFamily: 'AliBold', }, errorContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FFE5E5', borderRadius: 12, padding: 12, marginBottom: 16, }, errorText: { fontSize: 14, color: '#E54D4D', fontWeight: '600', marginLeft: 8, flex: 1, fontFamily: 'AliRegular', }, retryButton: { padding: 4, marginLeft: 8, }, viewMoreContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginBottom: 16, }, viewMoreText: { fontSize: 14, color: '#192126', fontFamily: 'AliRegular', }, viewMoreIcon: { fontSize: 16, color: '#192126', marginLeft: 4, fontFamily: 'AliRegular', }, stressCardRow: { flexDirection: 'row', justifyContent: 'flex-start', marginBottom: 16, }, healthCardsRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: 16, }, layoutContainer: { flex: 1, }, masonryContainer: { flexDirection: 'row', gap: 16, marginTop: 6, }, masonryColumn: { flex: 1, }, masonryCard: { width: '100%', backgroundColor: '#FFFFFF', borderRadius: 16, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 4, }, shadowOpacity: 0.12, shadowRadius: 12, elevation: 6, minHeight: 100, justifyContent: 'center', marginTop: 6 }, basalMetabolismCardOverride: { margin: -16, // 抵消 masonryCard 的 padding borderRadius: 16, }, stepsCardOverride: { margin: -16, // 抵消 masonryCard 的 padding borderRadius: 16, height: '100%', // 填充整个masonryCard }, workoutCardOverride: { marginTop: 16, }, waterCardOverride: { margin: -16, // 抵消 masonryCard 的 padding borderRadius: 16, height: '100%', // 填充整个masonryCard }, compactStepsCard: { minHeight: 100, }, stepsContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: 8, }, weightCard: { backgroundColor: '#F0F9FF', }, weightValue: { fontSize: 22, color: '#0369A1', fontWeight: '800', marginTop: 8, fontFamily: 'AliBold', }, addWeightButton: { position: 'absolute', right: 0, top: 0, padding: 4, }, circumferenceCard: { marginBottom: 36, marginTop: 16 }, sectionHeader: { marginTop: 24, paddingHorizontal: 4, }, sectionTitle: { fontSize: 18, fontWeight: '700', color: '#192126', textAlign: 'left', fontFamily: 'AliBold', }, headerActions: { flexDirection: 'row', alignItems: 'center', gap: 10, }, reportButton: { height: 36, borderRadius: 18, paddingHorizontal: 12, backgroundColor: '#F6F7FB', borderWidth: 1, borderColor: '#E5E7EB', flexDirection: 'row', alignItems: 'center', gap: 8, }, reportIconWrapper: { width: 28, height: 28, borderRadius: 14, alignItems: 'center', justifyContent: 'center', }, reportButtonLabel: { fontSize: 14, fontFamily: 'AliBold', color: '#0F172A', }, // Liquid Glass 风格按钮 liquidGlassButton: { height: 40, width: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center', overflow: 'hidden', }, liquidGlassFallback: { backgroundColor: 'rgba(255, 255, 255, 0.6)', borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.8)', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 8, elevation: 4, }, });