my-kaiqiuwang/src/components/ClubEventList.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

124 lines
4.8 KiB
TypeScript

import { Button, Divider, Empty, Flex, Pagination, Skeleton, Space, Switch, Typography } from "antd";
import type { IEventInfo } from "../types";
import { useCallback, useEffect, useMemo, useState } from "react";
import { EventCard } from "./EventCard";
import { useRequest } from "ahooks";
import { CalendarOutlined } from "@ant-design/icons";
import { STORE_PAGE_LIST_KEY } from "../utils/constants";
interface Props {
clubId: string;
}
const getStorageKey = (clubId: string) => `events-page-${clubId}`;
export const ClubEvenList = (props: Props) => {
const [paginationControlVisible, setPaginationControlVisible] = useState(true);
const [showFinishedEvents, setShowFinishedEvents] = useState(false);
const requestEvents = useRequest<{data: IEventInfo[]; total: number; }, [string, number]>(
async (clubId: string, page = 1) => {
if (!clubId) return { data: [], total: 0 };
return fetch(`/api/club/${clubId}/events?page=${page}`).then(e => e.json());
}, { manual: true, refreshDeps: [] });
const [page, setPageState] = useState(1);
const setPage = useCallback((page: number) => {
const key = getStorageKey(props.clubId);
sessionStorage.setItem(key, page.toString());
setPageState(page);
}, [props.clubId])
const onPageChange = useCallback((page: number) => {
setPage(page);
requestEvents.runAsync(props.clubId, page);
}, [props.clubId, setPage]);
const pagination = useMemo(() => {
return {
page,
onPageChange,
total: requestEvents.data?.total ?? 0,
}
}, [requestEvents.data?.total, page]);
const visibleEvents = useMemo(() => {
const events = requestEvents.data?.data || [];
if (showFinishedEvents) return events;
return events.filter(e => !e.isFinished);
}, [showFinishedEvents, requestEvents.data?.data]);
const onPageInit = useCallback(async () => {
const storePageKey = getStorageKey(props.clubId);
const keys: string[] = JSON.parse(localStorage.getItem(STORE_PAGE_LIST_KEY) ?? '[]');
if (!keys.includes(storePageKey)) {
keys.push(storePageKey);
localStorage.setItem(STORE_PAGE_LIST_KEY, JSON.stringify(keys));
}
const prePage = Number(sessionStorage.getItem(storePageKey)) || 1;
const data = await requestEvents.runAsync(props.clubId, prePage).then(res => res.data);
setPage(prePage);
if (data.length) {
const isAllFinishedOrAllNotFinished = data.every(e => e.isFinished) || data.every(e => !e.isFinished);
setShowFinishedEvents(prePage !== 1 || isAllFinishedOrAllNotFinished);
setPaginationControlVisible(prePage === 1 ? !isAllFinishedOrAllNotFinished : false);
}
}, [props.clubId]);
useEffect(() => {
const id = setTimeout(() => {
onPageInit();
}, 300);
return () => clearTimeout(id);
}, [onPageInit]);
const handleAddToCalendar = useCallback(() => {
const url = `${window.location.origin}/api/club/${props.clubId}/calendar.ics`;
const uri = url.replace(/^http(s)?/, 'webcal');
console.debug(uri);
window.open(uri);
}, [props.clubId, page]);
return (
<>
<Divider>
{paginationControlVisible ? (
<Space>
<Typography.Text type="secondary"></Typography.Text>
<Switch checked={showFinishedEvents} onChange={() => setShowFinishedEvents(!showFinishedEvents)} unCheckedChildren="隐藏" checkedChildren="显示" />
</Space>
) : null}
</Divider>
<Flex wrap vertical gap={12} justify="center" align="center">
<Button
type="link"
icon={<CalendarOutlined />}
onClick={handleAddToCalendar}
>
</Button>
{showFinishedEvents ? (
<Pagination
showQuickJumper
total={pagination.total}
current={pagination.page}
onChange={pagination.onPageChange}
showSizeChanger={false}
/>) : null}
{requestEvents.loading ? (
<Flex vertical gap={12} style={{ width: '100%', maxWidth: 600 }}>
<Skeleton.Button active style={{ height: 200 }} block size="large" />
<Skeleton.Button active style={{ height: 200 }} block size="large" />
<Skeleton.Button active style={{ height: 200 }} block size="large" />
</Flex>
) : (<>{visibleEvents.length ? visibleEvents.map(e => {
return (
<div key={e.matchId} style={{ width: '100%', maxWidth: 600 }}>
<EventCard eventInfo={e} />
</div>
);
}) : <Empty description="暂无活动" />}</>)}
{showFinishedEvents ? (
<Pagination
showQuickJumper
total={pagination.total}
current={pagination.page}
onChange={pagination.onPageChange}
showSizeChanger={false}
/>) : null}
</Flex>
</>
);
}