mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: better loading
This commit is contained in:
1 parent
76625dc7b2
commit
ee9dbdaf74
5 files changed
+65
-110
No files matched your search
+31
-13
@@ -1,4 +1,4 @@
|
|||||||
import { Flex, Heading, Text } from '@radix-ui/themes';
|
import { Flex, Heading, Skeleton, Text } from '@radix-ui/themes';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { WargamingLoginButton } from '../../../../../../../../../components/Navbar/components/WargamingLoginButton';
|
import { WargamingLoginButton } from '../../../../../../../../../components/Navbar/components/WargamingLoginButton';
|
||||||
import { formatCompact } from '../../../../../../../../../core/math/formatCompact';
|
import { formatCompact } from '../../../../../../../../../core/math/formatCompact';
|
||||||
@@ -19,9 +19,11 @@ export const Votes = memo(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="3">
|
<Flex direction="column" gap="3">
|
||||||
<Flex justify="between" align="end">
|
<Flex justify="between" align="center">
|
||||||
<Heading size="4">
|
<Heading size="4">
|
||||||
{formatCompact(votes.votes)} {votes.votes === 1 ? 'vote' : 'votes'}
|
{!votes && <Skeleton height="1em" width="4rem" />}
|
||||||
|
{votes && formatCompact(votes.votes)}{' '}
|
||||||
|
{votes && (votes.votes > 1 ? 'votes' : 'vote')}
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
{!wargaming && (
|
{!wargaming && (
|
||||||
@@ -37,8 +39,14 @@ export const Votes = memo(
|
|||||||
gap={{ initial: '0', sm: '6' }}
|
gap={{ initial: '0', sm: '6' }}
|
||||||
direction={{ initial: 'column', sm: 'row' }}
|
direction={{ initial: 'column', sm: 'row' }}
|
||||||
>
|
>
|
||||||
<StarRow stars={votes.categories.easiness} children="Easiness" />
|
<StarRow
|
||||||
<StarRow stars={votes.categories.firepower} children="Firepower" />
|
stars={votes?.categories.easiness ?? null}
|
||||||
|
children="Easiness"
|
||||||
|
/>
|
||||||
|
<StarRow
|
||||||
|
stars={votes?.categories.firepower ?? null}
|
||||||
|
children="Firepower"
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
@@ -46,24 +54,34 @@ export const Votes = memo(
|
|||||||
direction={{ initial: 'column', sm: 'row' }}
|
direction={{ initial: 'column', sm: 'row' }}
|
||||||
>
|
>
|
||||||
<StarRow
|
<StarRow
|
||||||
stars={votes.categories.maneuverability}
|
stars={votes?.categories.maneuverability ?? null}
|
||||||
children="Maneuverability"
|
children="Maneuverability"
|
||||||
/>
|
/>
|
||||||
<StarRow
|
<StarRow
|
||||||
stars={votes.categories.survivability}
|
stars={votes?.categories.survivability ?? null}
|
||||||
children="Survivability"
|
children="Survivability"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{wargaming && (
|
<Flex justify="center">
|
||||||
<Text align="center" size="2" color="gray" mt="2">
|
<Text size="2" color="gray" mt="2">
|
||||||
<>
|
<>
|
||||||
{votes.last_updated
|
{votes ? (
|
||||||
? `You voted on ${new Date(votes.last_updated).toLocaleDateString()}`
|
wargaming ? (
|
||||||
: `You haven't voted yet`}
|
votes.last_updated ? (
|
||||||
|
`You voted on ${new Date(votes.last_updated).toLocaleDateString()}`
|
||||||
|
) : (
|
||||||
|
`You haven't voted yet`
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
'Login to vote'
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Skeleton height="1em" width="5rem" />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
import { PaperPlaneIcon } from '@radix-ui/react-icons';
|
|
||||||
import { Button, Flex, Heading, Skeleton, Text } from '@radix-ui/themes';
|
|
||||||
import { WargamingLoginButton } from '../../../../../../../../components/Navbar/components/WargamingLoginButton';
|
|
||||||
import * as App from '../../../../../../../../stores/app';
|
|
||||||
|
|
||||||
export function VotesSkeleton() {
|
|
||||||
const wargaming = App.useDeferred(
|
|
||||||
undefined,
|
|
||||||
(state) => state.logins.wargaming,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex direction="column" gap="3">
|
|
||||||
<Flex justify="between" align="end">
|
|
||||||
<Heading size="4">
|
|
||||||
<Skeleton height="1em" width="6rem" />
|
|
||||||
</Heading>
|
|
||||||
|
|
||||||
{!wargaming && (
|
|
||||||
<WargamingLoginButton variant="outline">
|
|
||||||
Login to vote
|
|
||||||
</WargamingLoginButton>
|
|
||||||
)}
|
|
||||||
{wargaming && (
|
|
||||||
<Button variant="surface">
|
|
||||||
<PaperPlaneIcon /> Vote
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="0" direction="column">
|
|
||||||
<Flex gap="6">
|
|
||||||
<Flex align="center" justify="between" width="15rem">
|
|
||||||
<Text>Easiness</Text>
|
|
||||||
<Skeleton height="1em" width="5rem" />
|
|
||||||
</Flex>
|
|
||||||
<Flex align="center" justify="between" width="15rem">
|
|
||||||
<Text>Firepower</Text>
|
|
||||||
<Skeleton height="1em" width="5rem" />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
<Flex gap="6">
|
|
||||||
<Flex align="center" justify="between" width="15rem">
|
|
||||||
<Text>Maneuverability</Text>
|
|
||||||
<Skeleton height="1em" width="5rem" />
|
|
||||||
</Flex>
|
|
||||||
<Flex align="center" justify="between" width="15rem">
|
|
||||||
<Text>Survivability</Text>
|
|
||||||
<Skeleton height="1em" width="5rem" />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ChevronLeftIcon, MixIcon } from '@radix-ui/react-icons';
|
import { ChevronLeftIcon, MixIcon } from '@radix-ui/react-icons';
|
||||||
import { Button, Code, Flex } from '@radix-ui/themes';
|
import { Button, Code, Flex } from '@radix-ui/themes';
|
||||||
import { Suspense } from 'react';
|
|
||||||
import { classIcons } from '../../../../../../../components/ClassIcon';
|
import { classIcons } from '../../../../../../../components/ClassIcon';
|
||||||
import { Link } from '../../../../../../../components/Link';
|
import { Link } from '../../../../../../../components/Link';
|
||||||
import { asset } from '../../../../../../../core/blitzkit/asset';
|
import { asset } from '../../../../../../../core/blitzkit/asset';
|
||||||
@@ -9,7 +8,6 @@ import * as App from '../../../../../../../stores/app';
|
|||||||
import * as Duel from '../../../../../../../stores/duel';
|
import * as Duel from '../../../../../../../stores/duel';
|
||||||
import { Listing } from './components/Listing';
|
import { Listing } from './components/Listing';
|
||||||
import { Votes } from './components/Votes';
|
import { Votes } from './components/Votes';
|
||||||
import { VotesSkeleton } from './components/VotesSkeleton';
|
|
||||||
|
|
||||||
export function MetaSection() {
|
export function MetaSection() {
|
||||||
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
||||||
@@ -40,7 +38,10 @@ export function MetaSection() {
|
|||||||
</Link>
|
</Link>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex gap="6">
|
<Flex
|
||||||
|
gap={{ initial: '0', sm: '6' }}
|
||||||
|
direction={{ initial: 'column', sm: 'row' }}
|
||||||
|
>
|
||||||
<Flex direction="column" width="100%">
|
<Flex direction="column" width="100%">
|
||||||
{protagonist.nameFull && (
|
{protagonist.nameFull && (
|
||||||
<Listing label="Full-name">{protagonist.nameFull}</Listing>
|
<Listing label="Full-name">{protagonist.nameFull}</Listing>
|
||||||
@@ -139,9 +140,7 @@ export function MetaSection() {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Suspense fallback={<VotesSkeleton />}>
|
<Votes />
|
||||||
<Votes />
|
|
||||||
</Suspense>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
|
import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
|
||||||
import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
|
import { Flex, Heading, IconButton, ScrollArea, Text } from '@radix-ui/themes';
|
||||||
import { use, useEffect, useMemo, useRef, useState } from 'react';
|
import { Fragment, use, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { asset } from '../../../../../../../core/blitzkit/asset';
|
import { asset } from '../../../../../../../core/blitzkit/asset';
|
||||||
import { imgur } from '../../../../../../../core/blitzkit/imgur';
|
import { imgur } from '../../../../../../../core/blitzkit/imgur';
|
||||||
import { tankDefinitions } from '../../../../../../../core/blitzkit/tankDefinitions';
|
import { tankDefinitions } from '../../../../../../../core/blitzkit/tankDefinitions';
|
||||||
@@ -128,6 +128,7 @@ export function TechTreeSection() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
|
key={multiplier}
|
||||||
style={{
|
style={{
|
||||||
background: `url(${imgur('7hDltb4')})`,
|
background: `url(${imgur('7hDltb4')})`,
|
||||||
backgroundSize: 'contain',
|
backgroundSize: 'contain',
|
||||||
@@ -141,7 +142,6 @@ export function TechTreeSection() {
|
|||||||
draft.xpMultiplier = multiplier;
|
draft.xpMultiplier = multiplier;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
key={multiplier}
|
|
||||||
width="2.5rem"
|
width="2.5rem"
|
||||||
justify="center"
|
justify="center"
|
||||||
align="center"
|
align="center"
|
||||||
@@ -164,15 +164,15 @@ export function TechTreeSection() {
|
|||||||
const tank = awaitedTankDefinitions[id];
|
const tank = awaitedTankDefinitions[id];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Fragment key={id}>
|
||||||
{index > 0 && <Arrow />}
|
{index > 0 && <Arrow key={`${id}-arrow`} />}
|
||||||
<Node
|
<Node
|
||||||
key={id}
|
key={`${id}-node`}
|
||||||
id={id}
|
id={id}
|
||||||
nextIds={last ? tank.successors : [line[index + 1]]}
|
nextIds={last ? tank.successors : [line[index + 1]]}
|
||||||
highlight={last}
|
highlight={last}
|
||||||
/>
|
/>
|
||||||
</>
|
</Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
@@ -180,14 +180,14 @@ export function TechTreeSection() {
|
|||||||
<>
|
<>
|
||||||
<Arrow />
|
<Arrow />
|
||||||
{master.successors!.map((id, index) => (
|
{master.successors!.map((id, index) => (
|
||||||
<>
|
<Fragment key={id}>
|
||||||
{index > 0 && (
|
{index > 0 && (
|
||||||
<Text color="gray">
|
<Text color="gray" key={`${id}-plus`}>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Node key={id} id={id} />
|
<Node key={`${id}-node}`} id={id} />
|
||||||
</>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+19
-26
@@ -1,4 +1,4 @@
|
|||||||
import { use, useMemo } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { TankVotes } from '../app/api/tank-voting/[id]/average/route';
|
import { TankVotes } from '../app/api/tank-voting/[id]/average/route';
|
||||||
import { unwrapBlitzkitResponse } from '../core/blitzkit/unwrapBlitzkitResponse';
|
import { unwrapBlitzkitResponse } from '../core/blitzkit/unwrapBlitzkitResponse';
|
||||||
import * as App from '../stores/app';
|
import * as App from '../stores/app';
|
||||||
@@ -20,32 +20,25 @@ const cache: Record<string, Promise<TankVotes> | TankVotes> = {};
|
|||||||
|
|
||||||
export function useTankVotes(id: number) {
|
export function useTankVotes(id: number) {
|
||||||
const wargaming = App.use((state) => state.logins.wargaming);
|
const wargaming = App.use((state) => state.logins.wargaming);
|
||||||
const promise = useMemo(
|
const [votes, setVotes] = useState<TankVotes | null>(null);
|
||||||
() =>
|
|
||||||
new Promise<TankVotes>((resolve) => {
|
|
||||||
if (id in cache) {
|
|
||||||
resolve(cache[id]);
|
|
||||||
} else {
|
|
||||||
const cacheable = fetch(
|
|
||||||
`http://localhost:3000/api/tank-voting/${id}/average${
|
|
||||||
wargaming
|
|
||||||
? `?player=${wargaming.id}&token=${wargaming.token}`
|
|
||||||
: ''
|
|
||||||
}`,
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
(response) =>
|
|
||||||
response.json() as Promise<BlitzkitResponse<TankVotes>>,
|
|
||||||
)
|
|
||||||
.then(unwrapBlitzkitResponse);
|
|
||||||
cacheable.then(resolve);
|
|
||||||
|
|
||||||
cache[id] = cacheable;
|
useEffect(() => {
|
||||||
}
|
if (id in cache) {
|
||||||
}),
|
Promise.all([cache[id]]).then(([votes]) => setVotes(votes));
|
||||||
[id, wargaming],
|
} else {
|
||||||
);
|
const cacheable = fetch(
|
||||||
const votes = use(promise);
|
`/api/tank-voting/${id}/average${
|
||||||
|
wargaming ? `?player=${wargaming.id}&token=${wargaming.token}` : ''
|
||||||
|
}`,
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(response) => response.json() as Promise<BlitzkitResponse<TankVotes>>,
|
||||||
|
)
|
||||||
|
.then(unwrapBlitzkitResponse);
|
||||||
|
cacheable.then(setVotes);
|
||||||
|
cache[id] = cacheable;
|
||||||
|
}
|
||||||
|
}, [id, wargaming]);
|
||||||
|
|
||||||
return votes;
|
return votes;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user