feat(challenges): 新增挑战详情页与排行榜及轮播卡片交互
- 重构挑战列表为横向轮播,支持多进行中的挑战 - 新增挑战详情页 /challenges/[id]/index 与排行榜 /challenges/[id]/leaderboard - ChallengeProgressCard 支持小时级剩余时间显示 - 新增 ChallengeRankingItem 组件展示榜单项 - 排行榜支持分页加载、下拉刷新与错误重试 - 挑战卡片新增已结束角标与渐变遮罩 - 加入/退出挑战时展示庆祝动画与错误提示 - 统一背景渐变色与卡片阴影细节
This commit is contained in:
@@ -20,6 +20,11 @@ type ChallengeProgressCardProps = {
|
||||
inactiveColor?: string;
|
||||
};
|
||||
|
||||
type RemainingTime = {
|
||||
value: number;
|
||||
unit: '天' | '小时';
|
||||
};
|
||||
|
||||
const DEFAULT_BACKGROUND: [string, string] = ['#ffffff', '#ffffff'];
|
||||
const DEFAULT_TITLE_COLOR = '#1c1f3a';
|
||||
const DEFAULT_SUBTITLE_COLOR = '#707baf';
|
||||
@@ -38,11 +43,22 @@ const clampSegments = (target: number, completed: number) => {
|
||||
return { segmentsCount, completedSegments };
|
||||
};
|
||||
|
||||
const calculateRemainingDays = (endAt?: string) => {
|
||||
if (!endAt) return 0;
|
||||
const calculateRemainingTime = (endAt?: string): RemainingTime => {
|
||||
if (!endAt) return { value: 0, unit: '天' };
|
||||
const endDate = dayjs(endAt);
|
||||
if (!endDate.isValid()) return 0;
|
||||
return Math.max(0, endDate.diff(dayjs(), 'd'));
|
||||
if (!endDate.isValid()) return { value: 0, unit: '天' };
|
||||
|
||||
const diffMilliseconds = endDate.diff(dayjs());
|
||||
if (diffMilliseconds <= 0) {
|
||||
return { value: 0, unit: '天' };
|
||||
}
|
||||
|
||||
const diffHours = diffMilliseconds / (60 * 60 * 1000);
|
||||
if (diffHours < 24) {
|
||||
return { value: Math.max(1, Math.floor(diffHours)), unit: '小时' };
|
||||
}
|
||||
|
||||
return { value: Math.floor(diffHours / 24), unit: '天' };
|
||||
};
|
||||
|
||||
export const ChallengeProgressCard: React.FC<ChallengeProgressCardProps> = ({
|
||||
@@ -96,7 +112,7 @@ export const ChallengeProgressCard: React.FC<ChallengeProgressCardProps> = ({
|
||||
});
|
||||
}, [segments?.completedSegments, segments?.segmentsCount]);
|
||||
|
||||
const remainingDays = useMemo(() => calculateRemainingDays(endAt), [endAt]);
|
||||
const remainingTime = useMemo(() => calculateRemainingTime(endAt), [endAt]);
|
||||
|
||||
if (!hasValidProgress || !progress || !segments) {
|
||||
return null;
|
||||
@@ -111,7 +127,9 @@ export const ChallengeProgressCard: React.FC<ChallengeProgressCardProps> = ({
|
||||
{title}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={[styles.remaining, { color: subtitleColor }]}>挑战剩余 {remainingDays} 天</Text>
|
||||
<Text style={[styles.remaining, { color: subtitleColor }]}>
|
||||
挑战剩余 {remainingTime.value} {remainingTime.unit}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.metaRow}>
|
||||
|
||||
Reference in New Issue
Block a user