From b80af23f4f19afb5394c41da9b26a356eb028363 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Mon, 29 Sep 2025 09:59:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(challenges):=20=E4=BC=98=E5=8C=96=E6=8C=91?= =?UTF-8?q?=E6=88=98=E5=88=97=E8=A1=A8=E4=B8=8E=E8=AF=A6=E6=83=85=E9=A1=B5?= =?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 替换 Image 为 expo-image 并启用缓存策略 - 调整礼物按钮尺寸与图标大小 - 加入挑战失败时弹出 Toast 提示 - 统一异步流程并移除冗余状态监听 - 清理调试日志与多余空行 --- app/(tabs)/challenges.tsx | 13 ++++++++----- app/challenges/[id].tsx | 37 ++++++++++++++++++++++--------------- store/challengesSlice.ts | 9 +++++++-- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/app/(tabs)/challenges.tsx b/app/(tabs)/challenges.tsx index 8378651..aad1ec5 100644 --- a/app/(tabs)/challenges.tsx +++ b/app/(tabs)/challenges.tsx @@ -9,12 +9,12 @@ import { 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, - Image, ScrollView, StatusBar, StyleSheet, @@ -42,6 +42,9 @@ export default function ChallengesScreen() { const listStatus = useAppSelector(selectChallengesListStatus); const listError = useAppSelector(selectChallengesListError); + console.log('challenges', challenges); + + useEffect(() => { if (listStatus === 'idle') { dispatch(fetchChallenges()); @@ -124,7 +127,7 @@ export default function ChallengesScreen() { end={{ x: 1, y: 1 }} style={styles.giftButton} > - + @@ -162,7 +165,7 @@ function ChallengeCard({ challenge, surfaceColor, textColor, mutedColor, onPress @@ -252,8 +255,8 @@ const styles = StyleSheet.create({ borderRadius: 26, }, giftButton: { - width: 52, - height: 52, + width: 32, + height: 32, borderRadius: 26, alignItems: 'center', justifyContent: 'center', diff --git a/app/challenges/[id].tsx b/app/challenges/[id].tsx index a261c7e..574d1bd 100644 --- a/app/challenges/[id].tsx +++ b/app/challenges/[id].tsx @@ -17,6 +17,7 @@ import { selectProgressError, selectProgressStatus, } from '@/store/challengesSlice'; +import { Toast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; import { BlurView } from 'expo-blur'; import { LinearGradient } from 'expo-linear-gradient'; @@ -82,6 +83,7 @@ export default function ChallengeDetailScreen() { const challengeSelector = useMemo(() => (id ? selectChallengeById(id) : undefined), [id]); const challenge = useAppSelector((state) => (challengeSelector ? challengeSelector(state) : undefined)); + const detailStatusSelector = useMemo(() => (id ? selectChallengeDetailStatus(id) : undefined), [id]); const detailStatus = useAppSelector((state) => (detailStatusSelector ? detailStatusSelector(state) : 'idle')); const detailErrorSelector = useMemo(() => (id ? selectChallengeDetailError(id) : undefined), [id]); @@ -103,22 +105,22 @@ export default function ChallengeDetailScreen() { const progressError = useAppSelector((state) => (progressErrorSelector ? progressErrorSelector(state) : undefined)); useEffect(() => { - if (id) { - dispatch(fetchChallengeDetail(id)); + const getData = async (id: string) => { + try { + await dispatch(fetchChallengeDetail(id)).unwrap; + } catch (error) { + } } + + if (id) { + getData(id); + } + }, [dispatch, id]); + const [showCelebration, setShowCelebration] = useState(false); - useEffect(() => { - setShowCelebration(false); - }, [id]); - - useEffect(() => { - if (joinStatus === 'succeeded') { - setShowCelebration(true); - } - }, [joinStatus]); useEffect(() => { if (!showCelebration) { @@ -179,11 +181,16 @@ export default function ChallengeDetailScreen() { } }; - const handleJoin = () => { + const handleJoin = async () => { if (!id || joinStatus === 'loading') { return; } - dispatch(joinChallenge(id)); + try { + await dispatch(joinChallenge(id)); + setShowCelebration(true) + } catch (error) { + Toast.error('加入挑战失败') + } }; const handleLeave = () => { @@ -259,7 +266,7 @@ export default function ChallengeDetailScreen() { - + - + {participantsLabel} {participantAvatars.length ? ( diff --git a/store/challengesSlice.ts b/store/challengesSlice.ts index 3fe80a7..aa43e34 100644 --- a/store/challengesSlice.ts +++ b/store/challengesSlice.ts @@ -1,4 +1,3 @@ -import { createAsyncThunk, createSelector, createSlice } from '@reduxjs/toolkit'; import { type ChallengeDetailDto, type ChallengeListItemDto, @@ -11,6 +10,7 @@ import { listChallenges, reportChallengeProgress as reportChallengeProgressApi, } from '@/services/challengesApi'; +import { createAsyncThunk, createSelector, createSlice } from '@reduxjs/toolkit'; import type { RootState } from './index'; type AsyncStatus = 'idle' | 'loading' | 'succeeded' | 'failed'; @@ -81,8 +81,11 @@ export const fetchChallengeDetail = createAsyncThunk { try { - return await getChallengeDetail(id); + const ret = await getChallengeDetail(id); + + return ret; } catch (error) { + console.log('######', error); return rejectWithValue(toErrorMessage(error)); } } @@ -290,6 +293,8 @@ const formatMonthDay = (input: string | undefined): string | undefined => { }; const buildDateRangeLabel = (challenge: ChallengeEntity): string => { + console.log('!!!!!!', challenge); + const startLabel = formatMonthDay(challenge.startAt); const endLabel = formatMonthDay(challenge.endAt); if (startLabel && endLabel) {