my-kaiqiuwang/src/components/EventCard.tsx
kyuuseiryuu 7103c5f7e3 refactor(components): migrate to Ant Design Statistic.Timer and remove useRunOnce
- Deleted the custom `useRunOnce` hook as its functionality is no longer needed.
- Refactored `EventCard` to determine `type` (countup/countdown) dynamically based on the event state and current time.
- Updated `EventCard` to pass `type` and `format` separately to the `Statistic.Timer` component, removing the need for string formatting logic inside the component.
- Modified `ClubEventList` to add a 300ms delay on page initialization, ensuring the component is fully mounted before triggering the initial fetch logic.
2026-03-15 08:42:53 +09:00

101 lines
2.8 KiB
TypeScript

import { Button, Card, Statistic, Typography } from "antd";
import type { IEventInfo } from "../types";
import dayjs from "dayjs";
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import { EyeOutlined } from "@ant-design/icons";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useNavigate } from "react-router";
import type { TimerType } from "antd/lib/statistic/Timer";
dayjs.extend(utc);
dayjs.extend(timezone);
interface EventCardProps {
eventInfo: IEventInfo;
}
export function EventCard(props: EventCardProps) {
const { eventInfo: e } = props;
const day = useMemo(() => dayjs.tz(e.startDate, 'Asia/Tokyo'), [e]);
const navigate = useNavigate();
const handleView = useCallback(() => {
navigate(`/event/${e.matchId}`);
}, [e]);
const [messageFormat, setMessageFormat] = useState('');
const getStatisticProps = useCallback((): {
format: string;
type: TimerType;
} => {
if (e.isFinished) {
if (dayjs().diff(day, 'days') < 1) return {
type: 'countdown',
format: `已结束`,
};
return {
type: 'countup',
format: `已结束 DD 天`,
};
}
if (e.isProcessing) {
return {
type: 'countup',
format: '比赛进行中 HH:mm:ss',
}
}
if (dayjs().diff(day, 'days') === 0) {
return {
type: 'countdown',
format: `距离比赛开始还有 HH:mm:ss`,
};
}
return {
type: 'countdown',
format: `距离比赛开始还有 DD 天 HH:mm:ss`,
};
}, [e, day]);
const [statisticType, setStatisticType] = useState<TimerType>('countdown');
const updateMessageFormat = useCallback(() => {
const statistic = getStatisticProps();
setMessageFormat(statistic.format);
setStatisticType(statistic.type);
console.debug('format: %s', day.format(statistic.format), statistic);
}, [getStatisticProps])
useEffect(() => {
const timeout = day.toDate().getTime() - Date.now();
updateMessageFormat();
const id = setTimeout(() => {
updateMessageFormat();
}, timeout);
return () => clearTimeout(id);
}, [updateMessageFormat]);
return (
<Card
key={e.matchId}
title={e.title}
style={{ width: '100%' }}
actions={[
<Button
type="link"
onClick={handleView}
icon={<EyeOutlined />}
>
</Button>,
]}
>
<Typography.Text type={e.isFinished ? 'secondary' : 'success'}>{e.title}</Typography.Text>
<Statistic.Timer
type={statisticType}
value={day.toDate().getTime()}
format={messageFormat}
styles={{ content: e.isFinished ? { color: 'gray' } : {} }}
/>
{e.info.map(e => (
<div key={e}>
<Typography.Text type='secondary'>{e}</Typography.Text>
</div>
))}
</Card>
)
}