import { IconSymbol } from '@/components/ui/IconSymbol'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; import { fetchChallenges, selectChallengeCards, selectChallengesListError, selectChallengesListStatus, type ChallengeCardViewModel, } from '@/store/challengesSlice'; import { Image } from 'expo-image'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import React, { useEffect } from 'react'; import { ActivityIndicator, ScrollView, StatusBar, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; const AVATAR_SIZE = 36; const CARD_IMAGE_WIDTH = 132; const CARD_IMAGE_HEIGHT = 96; const STATUS_LABELS: Record<'upcoming' | 'ongoing' | 'expired', string> = { upcoming: '即将开始', ongoing: '进行中', expired: '已结束', }; export default function ChallengesScreen() { const theme = (useColorScheme() ?? 'light') as 'light' | 'dark'; const colorTokens = Colors[theme]; const router = useRouter(); const dispatch = useAppDispatch(); const challenges = useAppSelector(selectChallengeCards); const listStatus = useAppSelector(selectChallengesListStatus); const listError = useAppSelector(selectChallengesListError); console.log('challenges', challenges); useEffect(() => { if (listStatus === 'idle') { dispatch(fetchChallenges()); } }, [dispatch, listStatus]); const gradientColors: [string, string] = theme === 'dark' ? ['#1f2230', '#10131e'] : [colorTokens.backgroundGradientStart, colorTokens.backgroundGradientEnd]; const renderChallenges = () => { if (listStatus === 'loading' && challenges.length === 0) { return ( 加载挑战中… ); } if (listStatus === 'failed' && challenges.length === 0) { return ( {listError ?? '加载挑战失败,请稍后重试'} dispatch(fetchChallenges())} > 重新加载 ); } if (challenges.length === 0) { return ( 暂无挑战,稍后再来探索。 ); } return challenges.map((challenge) => ( router.push({ pathname: '/challenges/[id]', params: { id: challenge.id } }) } /> )); }; return ( 挑战 参与精选活动,保持每日动力 {renderChallenges()} ); } type ChallengeCardProps = { challenge: ChallengeCardViewModel; surfaceColor: string; textColor: string; mutedColor: string; onPress: () => void; }; function ChallengeCard({ challenge, surfaceColor, textColor, mutedColor, onPress }: ChallengeCardProps) { const statusLabel = STATUS_LABELS[challenge.status] ?? challenge.status; return ( {challenge.title} {challenge.dateRange} {challenge.participantsLabel} {' · '} {statusLabel} {challenge.isJoined ? ' · 已加入' : ''} {challenge.progress?.badge ? ( {challenge.progress.badge} ) : null} {challenge.avatars.length ? ( ) : null} ); } type AvatarStackProps = { avatars: string[]; borderColor: string; }; function AvatarStack({ avatars, borderColor }: AvatarStackProps) { return ( {avatars .filter(Boolean) .map((avatar, index) => ( ))} ); } const styles = StyleSheet.create({ screen: { flex: 1, }, safeArea: { flex: 1, }, scrollContent: { paddingHorizontal: 20, paddingBottom: 32, }, headerRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 8, marginBottom: 26, }, title: { fontSize: 32, fontWeight: '700', letterSpacing: 1, }, subtitle: { marginTop: 6, fontSize: 14, fontWeight: '500', opacity: 0.8, }, giftShadow: { shadowColor: 'rgba(94, 62, 199, 0.45)', shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.35, shadowRadius: 12, elevation: 8, borderRadius: 26, }, giftButton: { width: 32, height: 32, borderRadius: 26, alignItems: 'center', justifyContent: 'center', }, cardsContainer: { gap: 18, }, stateContainer: { alignItems: 'center', justifyContent: 'center', paddingVertical: 40, paddingHorizontal: 20, }, stateText: { marginTop: 12, fontSize: 14, textAlign: 'center', lineHeight: 20, }, retryButton: { marginTop: 16, paddingHorizontal: 18, paddingVertical: 8, borderRadius: 18, borderWidth: StyleSheet.hairlineWidth, }, retryText: { fontSize: 13, fontWeight: '600', }, card: { flexDirection: 'row', borderRadius: 28, padding: 18, alignItems: 'center', shadowOffset: { width: 0, height: 16 }, shadowOpacity: 0.18, shadowRadius: 24, elevation: 6, }, cardImage: { width: CARD_IMAGE_WIDTH, height: CARD_IMAGE_HEIGHT, borderRadius: 22, }, cardContent: { flex: 1, marginLeft: 16, }, cardTitle: { fontSize: 18, fontWeight: '700', marginBottom: 4, }, cardDate: { fontSize: 13, fontWeight: '500', marginBottom: 4, }, cardParticipants: { fontSize: 13, fontWeight: '500', }, cardProgress: { marginTop: 8, fontSize: 13, fontWeight: '600', }, avatarRow: { flexDirection: 'row', marginTop: 16, alignItems: 'center', }, avatar: { width: AVATAR_SIZE, height: AVATAR_SIZE, borderRadius: AVATAR_SIZE / 2, borderWidth: 2, }, avatarOffset: { marginLeft: -12, }, });