From 18d83091a95bd35a0e529ba7716d16cfa0ec8505 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Thu, 27 Nov 2025 11:11:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(challenges):=20=E6=B7=BB=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E6=8C=91=E6=88=98=E7=B1=BB=E5=9E=8B=E5=B9=B6?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AD=97=E6=AE=B5=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 CUSTOM 挑战类型支持 - 移除 requirementLabel 必填验证,改为可选字段 - 添加挑战类型选择器的编辑模式禁用状态 - 优化日期选择器的多语言支持 - 完善中英文国际化文案 - 修复空 requirementLabel 导致的渲染问题 --- app/challenges/[id]/index.tsx | 4 ++-- app/challenges/create-custom.tsx | 28 +++++++++++++++++++--------- i18n/index.ts | 15 +++++++++++++++ services/challengesApi.ts | 1 + 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/app/challenges/[id]/index.tsx b/app/challenges/[id]/index.tsx index 8e07a3e..f72e5a8 100644 --- a/app/challenges/[id]/index.tsx +++ b/app/challenges/[id]/index.tsx @@ -595,7 +595,7 @@ export default function ChallengeDetailScreen() { - {challenge.requirementLabel} + {challenge.requirementLabel ? {challenge.requirementLabel} : null} {t('challengeDetail.shareCard.info.checkInDaily')} @@ -743,7 +743,7 @@ export default function ChallengeDetailScreen() { - {challenge.requirementLabel} + {challenge.requirementLabel ? {challenge.requirementLabel} : null} {t('challengeDetail.detail.requirement')} diff --git a/app/challenges/create-custom.tsx b/app/challenges/create-custom.tsx index 9e093af..12c72de 100644 --- a/app/challenges/create-custom.tsx +++ b/app/challenges/create-custom.tsx @@ -1,3 +1,4 @@ +import i18n from '@/i18n'; import dayjs from 'dayjs'; import { BlurView } from 'expo-blur'; import * as Clipboard from 'expo-clipboard'; @@ -53,6 +54,7 @@ const getTypeOptions = (t: (key: string) => string): { value: ChallengeType; lab { value: ChallengeType.SLEEP, label: t('challenges.createCustom.typeLabels.sleep'), accent: '#7C3AED' }, { value: ChallengeType.MOOD, label: t('challenges.createCustom.typeLabels.mood'), accent: '#F97316' }, { value: ChallengeType.WEIGHT, label: t('challenges.createCustom.typeLabels.weight'), accent: '#22C55E' }, + { value: ChallengeType.CUSTOM, label: t('challenges.createCustom.typeLabels.custom'), accent: '#8B5CF6' }, ]; const FALLBACK_IMAGE = @@ -97,7 +99,7 @@ export default function CreateCustomChallengeScreen() { const [progressUnit, setProgressUnit] = useState(''); const [periodLabel, setPeriodLabel] = useState(''); const [periodEdited, setPeriodEdited] = useState(false); - const [rankingDescription] = useState('连续打卡榜'); + const [rankingDescription] = useState(t('challenges.createCustom.rankingDescription')); const [isPublic, setIsPublic] = useState(true); const [maxParticipants, setMaxParticipants] = useState('100'); const [minimumEdited, setMinimumEdited] = useState(false); @@ -121,7 +123,6 @@ export default function CreateCustomChallengeScreen() { setEndDate(new Date(challenge.endAt || Date.now())); setTargetValue(String(challenge.progress?.target || '')); setMinimumCheckInDays(String(challenge.minimumCheckInDays || '')); - setRequirementLabel(challenge.requirementLabel || ''); setSummary(challenge.summary || ''); setProgressUnit(challenge.unit || ''); setPeriodLabel(challenge.periodLabel || ''); @@ -177,10 +178,6 @@ export default function CreateCustomChallengeScreen() { return; } - if (!requirementLabel.trim()) { - Toast.warning(t('challenges.createCustom.alerts.requirementRequired')); - return; - } const startTimestamp = dayjs(startDate).valueOf(); const endTimestamp = dayjs(endDate).valueOf(); @@ -221,7 +218,7 @@ export default function CreateCustomChallengeScreen() { targetValue: target, minimumCheckInDays: minDays, durationLabel, - requirementLabel: requirementLabel.trim(), + requirementLabel: '', summary: summary.trim() || undefined, progressUnit: progressUnit.trim(), periodLabel: periodLabel.trim() || undefined, @@ -513,16 +510,19 @@ export default function CreateCustomChallengeScreen() { setType(option.value)} + onPress={() => !isEditMode && setType(option.value)} + disabled={isEditMode} style={[ styles.chip, active && { backgroundColor: `${option.accent}1A`, borderColor: option.accent }, + isEditMode && styles.chipDisabled, ]} > {option.label} @@ -531,6 +531,7 @@ export default function CreateCustomChallengeScreen() { ); })} + {t('challenges.createCustom.fields.challengeTypeHelper')} @@ -592,7 +593,6 @@ export default function CreateCustomChallengeScreen() { {renderField(t('challenges.createCustom.fields.minimumCheckInDays'), minimumCheckInDays, handleMinimumDaysChange, t('challenges.createCustom.fields.minimumCheckInDaysPlaceholder'), 'numeric')} - {renderField(t('challenges.createCustom.fields.challengeRequirement'), requirementLabel, setRequirementLabel, t('challenges.createCustom.fields.requirementPlaceholder'))} @@ -675,6 +675,9 @@ export default function CreateCustomChallengeScreen() { minimumDate={pickerType === 'end' ? dayjs(startDate).add(1, 'day').toDate() : dayjs().add(1, 'day').toDate()} onConfirm={handleConfirmDate} onCancel={() => setPickerType(null)} + locale={i18n.language} + confirmTextIOS={t('challenges.createCustom.datePicker.confirm')} + cancelTextIOS={t('challenges.createCustom.datePicker.cancel')} />