feat(challenges): 新增 ChallengeProgressCard 组件并接入喝水挑战进度上报
- 抽离进度卡片为独立组件,支持主题色自定义与复用 - 挑战列表页顶部展示进行中的挑战进度 - 喝水记录自动上报至关联的水挑战 - 移除旧版 challengeSlice 与冗余进度样式 - 统一使用 value 字段上报进度,兼容多类型挑战
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import ChallengeProgressCard from '@/components/challenges/ChallengeProgressCard';
|
||||
import { IconSymbol } from '@/components/ui/IconSymbol';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
import { Image } from 'expo-image';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { useRouter } from 'expo-router';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
ScrollView,
|
||||
@@ -41,9 +42,15 @@ export default function ChallengesScreen() {
|
||||
const challenges = useAppSelector(selectChallengeCards);
|
||||
const listStatus = useAppSelector(selectChallengesListStatus);
|
||||
const listError = useAppSelector(selectChallengesListError);
|
||||
|
||||
console.log('challenges', challenges);
|
||||
|
||||
const ongoingChallenge = useMemo(
|
||||
() =>
|
||||
challenges.find(
|
||||
(challenge) => challenge.status === 'ongoing' && challenge.isJoined && challenge.progress
|
||||
),
|
||||
[challenges]
|
||||
);
|
||||
const progressTrackColor = theme === 'dark' ? 'rgba(255, 255, 255, 0.08)' : '#eceffa';
|
||||
const progressInactiveColor = theme === 'dark' ? 'rgba(255, 255, 255, 0.24)' : '#dfe4f6';
|
||||
|
||||
useEffect(() => {
|
||||
if (listStatus === 'idle') {
|
||||
@@ -132,6 +139,30 @@ export default function ChallengesScreen() {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{ongoingChallenge ? (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.92}
|
||||
onPress={() =>
|
||||
router.push({ pathname: '/challenges/[id]', params: { id: ongoingChallenge.id } })
|
||||
}
|
||||
>
|
||||
<ChallengeProgressCard
|
||||
title={ongoingChallenge.title}
|
||||
endAt={ongoingChallenge.endAt}
|
||||
progress={ongoingChallenge.progress}
|
||||
style={styles.progressCardWrapper}
|
||||
backgroundColors={[colorTokens.card, colorTokens.card]}
|
||||
titleColor={colorTokens.text}
|
||||
subtitleColor={colorTokens.textSecondary}
|
||||
metaColor={colorTokens.primary}
|
||||
metaSuffixColor={colorTokens.textSecondary}
|
||||
accentColor={colorTokens.primary}
|
||||
trackColor={progressTrackColor}
|
||||
inactiveColor={progressInactiveColor}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
) : null}
|
||||
|
||||
<View style={styles.cardsContainer}>{renderChallenges()}</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
@@ -179,11 +210,6 @@ function ChallengeCard({ challenge, surfaceColor, textColor, mutedColor, onPress
|
||||
{statusLabel}
|
||||
{challenge.isJoined ? ' · 已加入' : ''}
|
||||
</Text>
|
||||
{challenge.progress?.badge ? (
|
||||
<Text style={[styles.cardProgress, { color: textColor }]} numberOfLines={1}>
|
||||
{challenge.progress.badge}
|
||||
</Text>
|
||||
) : null}
|
||||
{challenge.avatars.length ? (
|
||||
<AvatarStack avatars={challenge.avatars} borderColor={surfaceColor} />
|
||||
) : null}
|
||||
@@ -264,6 +290,9 @@ const styles = StyleSheet.create({
|
||||
cardsContainer: {
|
||||
gap: 18,
|
||||
},
|
||||
progressCardWrapper: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
stateContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
Reference in New Issue
Block a user