feat(global): HydrateFallback
This commit is contained in:
parent
bf74e99a47
commit
f37be8aded
@ -1,8 +1,8 @@
|
|||||||
import { Card, Divider, Flex, Select, Space, Statistic, Switch, Typography } from 'antd';
|
import { Card, Divider, Flex, Select, Skeleton, Space, Statistic, Switch, Typography } from 'antd';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import { clubs } from './clubList';
|
import { clubs } from './clubList';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import type { IEventInfo } from '../../types';
|
import type { IEventInfo } from '../../types';
|
||||||
|
|
||||||
@ -11,25 +11,25 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const GameSelector: React.FC<Props> = props => {
|
export const GameSelector: React.FC<Props> = props => {
|
||||||
const requestEvents = useRequest<IEventInfo[], [string]>(
|
const [clubId, setClubId] = useState<string>(clubs[0]?.clubId ?? '');
|
||||||
async (clubId: string) => (await fetch(`/api/events/${clubId}`)).json()
|
const requestEvents = useRequest<IEventInfo[], []>(
|
||||||
, { manual: true })
|
async () => {
|
||||||
const [gameList, setGameList] = useState<(IEventInfo & { finished: boolean })[]>([]);
|
if (!clubId) return [];
|
||||||
const [isEmpty, setIsEmpty] = useState(false);
|
return (await fetch(`/api/events/${clubId}`)).json()
|
||||||
const [clubId, setClubId] = useState(clubs[0].clubId);
|
}, { manual: false, refreshDeps: [clubId] })
|
||||||
const [showFinished, setShowFinished] = useState(false);
|
const [showFinished, setShowFinished] = useState(false);
|
||||||
const handleClubChange = useCallback(async (clubId: string) => {
|
const gameList = useMemo(() => {
|
||||||
const list = await requestEvents.runAsync(clubId);
|
const activeList = requestEvents.data?.map(e => ({
|
||||||
const activeList = list.map(e => ({
|
|
||||||
...e,
|
...e,
|
||||||
finished: e.info.join('').includes('已结束'),
|
finished: e.info.join('').includes('已结束'),
|
||||||
}));
|
}));
|
||||||
setGameList(activeList);
|
return activeList;
|
||||||
setIsEmpty(activeList.filter(e => !e.finished).length === 0);
|
}, [requestEvents.data]);
|
||||||
}, []);
|
const isEmpty = useMemo(() => {
|
||||||
useEffect(() => {
|
return (gameList ?? []).filter(e => !e.finished).length === 0
|
||||||
const clubId = clubs[0].clubId;
|
}, [gameList]);
|
||||||
handleClubChange(clubId);
|
const handleClubChange = useCallback(async (id: string) => {
|
||||||
|
setClubId(id);
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Space orientation='vertical' style={{ width: '100%' }}>
|
<Space orientation='vertical' style={{ width: '100%' }}>
|
||||||
@ -48,12 +48,14 @@ export const GameSelector: React.FC<Props> = props => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Divider>{isEmpty && (<Typography.Text type='secondary'>没有未开始的比赛</Typography.Text>)}</Divider>
|
<Divider>{isEmpty && (<Typography.Text type='secondary'>没有未开始的比赛</Typography.Text>)}</Divider>
|
||||||
|
{requestEvents.loading ? <Skeleton.Button active block style={{ height: 300 }} /> : (
|
||||||
<Flex wrap gap={12} justify='center'>
|
<Flex wrap gap={12} justify='center'>
|
||||||
{gameList
|
{gameList
|
||||||
.filter(e => showFinished || !e.finished)
|
?.filter(e => showFinished || !e.finished)
|
||||||
.map(e => <EventCard key={e.matchId} eventInfo={e} onGameClick={props.onGameClick} />)
|
?.map(e => <EventCard key={e.matchId} eventInfo={e} onGameClick={props.onGameClick} />)
|
||||||
}
|
}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,4 +3,4 @@ export const clubs = [
|
|||||||
name: '东华',
|
name: '东华',
|
||||||
clubId: '47',
|
clubId: '47',
|
||||||
},
|
},
|
||||||
] as const;
|
];
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { Flex, Form, InputNumber, Space, Switch, Typography } from "antd";
|
import { Flex, Form, InputNumber, Switch } from "antd";
|
||||||
import { chunk } from 'lodash';
|
import { chunk } from 'lodash';
|
||||||
import type { BasePlayer } from "../types";
|
import type { BasePlayer } from "../types";
|
||||||
import { GroupMember } from "./GroupMember";
|
import { GroupMember } from "./GroupMember";
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { useRequest } from "ahooks";
|
import { useRequest } from "ahooks";
|
||||||
import { Divider, Flex, Skeleton, Tag, Typography } from "antd";
|
import { Divider, Flex, Skeleton, Tag, Typography } from "antd";
|
||||||
import { EType, type XCXTag } from "../types";
|
import { EType, type XCXTag } from "../types";
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
uid?: string;
|
uid?: string;
|
||||||
|
|||||||
@ -5,40 +5,65 @@
|
|||||||
* It is included in `src/index.html`.
|
* It is included in `src/index.html`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { App } from "./App";
|
import { App } from "./App";
|
||||||
import { ConfigProvider, theme } from "antd";
|
import { ConfigProvider, Spin, theme } from "antd";
|
||||||
import { createBrowserRouter, RouterProvider } from "react-router";
|
import { createBrowserRouter, RouterProvider } from "react-router";
|
||||||
import ProfilePage from "./page/ProfilePage";
|
import ProfilePage from "./page/ProfilePage";
|
||||||
import EventPage from "./page/EventPage";
|
import EventPage from "./page/EventPage";
|
||||||
import type { MatchInfo } from "./types";
|
import type { MatchInfo } from "./types";
|
||||||
|
import { Outlet, useNavigation } from "react-router";
|
||||||
|
|
||||||
const elem = document.getElementById("root")!;
|
const elem = document.getElementById("root")!;
|
||||||
|
|
||||||
const route = createBrowserRouter([
|
const route = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
element: <App />
|
element: <Layout />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
index: true,
|
||||||
|
element: <App />,
|
||||||
|
HydrateFallback: () => <HydrateFallback />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/event/:matchId',
|
path: 'event/:matchId',
|
||||||
loader: async ({ params }) => {
|
loader: async ({ params }) => {
|
||||||
const info: MatchInfo = await (await fetch(`/api/match/${params.matchId}`)).json();
|
const info: MatchInfo = await (await fetch(`/api/match/${params.matchId}`)).json();
|
||||||
const members = await (await fetch(`/api/match/${params.matchId}/${info.itemId}`)).json();
|
const members = await (await fetch(`/api/match/${params.matchId}/${info.itemId}`)).json();
|
||||||
return { info, members };
|
return { info, members };
|
||||||
},
|
},
|
||||||
Component: EventPage,
|
Component: EventPage,
|
||||||
|
HydrateFallback: () => <HydrateFallback />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/profile/:uid',
|
path: 'profile/:uid',
|
||||||
loader: async ({ params }) => {
|
loader: async ({ params }) => {
|
||||||
return fetch(`/api/user/${params.uid}`);
|
return fetch(`/api/user/${params.uid}`);
|
||||||
},
|
},
|
||||||
Component: ProfilePage,
|
Component: ProfilePage,
|
||||||
|
HydrateFallback: () => <HydrateFallback />
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
function HydrateFallback() {
|
||||||
|
return (
|
||||||
|
<Spin spinning>
|
||||||
|
<div style={{ height: '100vh' }} />
|
||||||
|
</Spin>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Layout() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const loading = navigation.state === 'loading';
|
||||||
|
return loading ? <HydrateFallback /> : <Outlet />
|
||||||
|
}
|
||||||
|
|
||||||
const app = (
|
const app = (
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<ConfigProvider theme={{
|
<ConfigProvider theme={{
|
||||||
|
|||||||
@ -80,6 +80,7 @@ function PlayerList(props: { title: string; names?: string[]; uids?: string[] })
|
|||||||
|
|
||||||
export default function ProfilePage() {
|
export default function ProfilePage() {
|
||||||
const profile = useLoaderData<XCXProfile | null>();
|
const profile = useLoaderData<XCXProfile | null>();
|
||||||
|
console.debug('profile', profile);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user