import { HeaderBar } from '@/components/ui/HeaderBar'; import { palette } from '@/constants/Colors'; import { useMembershipModal } from '@/contexts/MembershipModalContext'; import { useToast } from '@/contexts/ToastContext'; import { useI18n } from '@/hooks/useI18n'; import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding'; import { useVipService } from '@/hooks/useVipService'; import { getStatisticsCardOrder, getStatisticsCardsVisibility, setStatisticsCardOrder, setStatisticsCardVisibility, StatisticsCardsVisibility } from '@/utils/userPreferences'; import { Ionicons } from '@expo/vector-icons'; import { LinearGradient } from 'expo-linear-gradient'; import { router, useFocusEffect } from 'expo-router'; import React, { useCallback, useState } from 'react'; import { StatusBar, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native'; import DraggableFlatList, { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; type CardItem = { key: string; title: string; icon: keyof typeof Ionicons.glyphMap; visible: boolean; visibilityKey: keyof StatisticsCardsVisibility; }; export default function StatisticsCustomizationScreen() { const safeAreaTop = useSafeAreaTop(60); const { t } = useI18n(); const { isVip } = useVipService(); const { openMembershipModal } = useMembershipModal(); const { showToast } = useToast(); const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState([]); const CARD_CONFIG: Record = { mood: { icon: 'happy-outline', titleKey: 'statisticsCustomization.items.mood', visibilityKey: 'showMood' }, steps: { icon: 'footsteps-outline', titleKey: 'statisticsCustomization.items.steps', visibilityKey: 'showSteps' }, stress: { icon: 'pulse-outline', titleKey: 'statisticsCustomization.items.stress', visibilityKey: 'showStress' }, sleep: { icon: 'moon-outline', titleKey: 'statisticsCustomization.items.sleep', visibilityKey: 'showSleep' }, fitness: { icon: 'fitness-outline', titleKey: 'statisticsCustomization.items.fitnessRings', visibilityKey: 'showFitnessRings' }, water: { icon: 'water-outline', titleKey: 'statisticsCustomization.items.water', visibilityKey: 'showWater' }, metabolism: { icon: 'flame-outline', titleKey: 'statisticsCustomization.items.basalMetabolism', visibilityKey: 'showBasalMetabolism' }, oxygen: { icon: 'water-outline', titleKey: 'statisticsCustomization.items.oxygenSaturation', visibilityKey: 'showOxygenSaturation' }, temperature: { icon: 'thermometer-outline', titleKey: 'statisticsCustomization.items.wristTemperature', visibilityKey: 'showWristTemperature' }, menstrual: { icon: 'rose-outline', titleKey: 'statisticsCustomization.items.menstrualCycle', visibilityKey: 'showMenstrualCycle' }, weight: { icon: 'scale-outline', titleKey: 'statisticsCustomization.items.weight', visibilityKey: 'showWeight' }, circumference: { icon: 'body-outline', titleKey: 'statisticsCustomization.items.circumference', visibilityKey: 'showCircumference' }, }; // 加载设置 const loadSettings = useCallback(async () => { try { const [visibility, order] = await Promise.all([ getStatisticsCardsVisibility(), getStatisticsCardOrder(), ]); // 确保 order 包含所有配置的 key (处理新增 key 的情况) const allKeys = Object.keys(CARD_CONFIG); const uniqueOrder = Array.from(new Set([...order, ...allKeys])); const listData: CardItem[] = uniqueOrder .filter(key => CARD_CONFIG[key]) // 过滤掉无效 key .map(key => { const config = CARD_CONFIG[key]; return { key, title: t(config.titleKey), icon: config.icon, visible: visibility[config.visibilityKey], visibilityKey: config.visibilityKey, }; }); setData(listData); } catch (error) { console.error('Failed to load statistics customization settings:', error); } finally { setIsLoading(false); } }, [t]); // 页面聚焦时加载设置 useFocusEffect( useCallback(() => { loadSettings(); }, [loadSettings]) ); // 处理开关切换 const handleToggle = async (item: CardItem, value: boolean) => { if (!isVip) { showToast({ type: 'info', message: t('statisticsCustomization.vipRequired'), }); openMembershipModal(); return; } try { // 乐观更新 UI setData(prev => prev.map(d => d.key === item.key ? { ...d, visible: value } : d)); await setStatisticsCardVisibility(item.visibilityKey, value); } catch (error) { console.error(`Failed to set ${item.key}:`, error); // 回滚 setData(prev => prev.map(d => d.key === item.key ? { ...d, visible: !value } : d)); } }; // 处理排序结束 const handleDragEnd = async ({ data: newData }: { data: CardItem[] }) => { setData(newData); const newOrder = newData.map(item => item.key); try { await setStatisticsCardOrder(newOrder); } catch (error) { console.error('Failed to save card order:', error); } }; const renderItem = useCallback(({ item, drag, isActive }: RenderItemParams) => { const handleDrag = () => { if (!isVip) { showToast({ type: 'info', message: t('statisticsCustomization.vipRequired'), }); openMembershipModal(); return; } drag(); }; return ( {item.title} handleToggle(item, v)} trackColor={{ false: '#E5E5E5', true: '#9370DB' }} thumbColor="#FFFFFF" style={styles.switch} /> ); }, [handleToggle, isVip, t, showToast, openMembershipModal]); if (isLoading) { return ( {t('notificationSettings.loading')} ); } return ( router.back()} /> item.key} renderItem={renderItem} contentContainerStyle={[ styles.scrollContent, { paddingTop: safeAreaTop } ]} showsVerticalScrollIndicator={false} ListHeaderComponent={() => ( <> {t('notificationSettings.sections.description')} {t('statisticsCustomization.description.text')} {t('statisticsCustomization.sectionTitle')} )} /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#F5F5F5', }, gradientBackground: { position: 'absolute', left: 0, right: 0, top: 0, height: '60%', }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, loadingText: { fontSize: 16, color: '#666', }, scrollContent: { paddingHorizontal: 16, paddingBottom: 40, }, headerSection: { marginBottom: 20, }, subtitle: { fontSize: 14, color: '#6C757D', marginBottom: 12, marginLeft: 4, }, descriptionCard: { backgroundColor: 'rgba(255, 255, 255, 0.6)', borderRadius: 12, padding: 12, gap: 8, borderWidth: 1, borderColor: 'rgba(147, 112, 219, 0.1)', }, hintRow: { flexDirection: 'row', alignItems: 'center', gap: 8, }, descriptionText: { flex: 1, fontSize: 13, color: '#2C3E50', lineHeight: 18, }, sectionHeader: { marginBottom: 12, marginLeft: 4, }, sectionTitle: { fontSize: 16, fontWeight: '600', color: '#2C3E50', }, rowItem: { backgroundColor: '#FFFFFF', borderRadius: 16, marginBottom: 12, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.03, shadowRadius: 4, elevation: 2, }, activeItem: { backgroundColor: '#FAFAFA', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 8, elevation: 4, zIndex: 100, transform: [{ scale: 1.02 }], }, itemContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', padding: 16, height: 72, }, leftContent: { flexDirection: 'row', alignItems: 'center', flex: 1, }, dragHandle: { paddingRight: 12, }, iconContainer: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(147, 112, 219, 0.05)', borderRadius: 12, marginRight: 12, }, itemTitle: { fontSize: 16, fontWeight: '500', color: '#2C3E50', flex: 1, }, switch: { transform: [{ scaleX: 0.9 }, { scaleY: 0.9 }], }, });