mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +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 { WargamingLoginButton } from '../../../../../../../../../components/Navbar/components/WargamingLoginButton';
|
||||
import { formatCompact } from '../../../../../../../../../core/math/formatCompact';
|
||||
@@ -19,9 +19,11 @@ export const Votes = memo(
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="3">
|
||||
<Flex justify="between" align="end">
|
||||
<Flex justify="between" align="center">
|
||||
<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>
|
||||
|
||||
{!wargaming && (
|
||||
@@ -37,8 +39,14 @@ export const Votes = memo(
|
||||
gap={{ initial: '0', sm: '6' }}
|
||||
direction={{ initial: 'column', sm: 'row' }}
|
||||
>
|
||||
<StarRow stars={votes.categories.easiness} children="Easiness" />
|
||||
<StarRow stars={votes.categories.firepower} children="Firepower" />
|
||||
<StarRow
|
||||
stars={votes?.categories.easiness ?? null}
|
||||
children="Easiness"
|
||||
/>
|
||||
<StarRow
|
||||
stars={votes?.categories.firepower ?? null}
|
||||
children="Firepower"
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
@@ -46,24 +54,34 @@ export const Votes = memo(
|
||||
direction={{ initial: 'column', sm: 'row' }}
|
||||
>
|
||||
<StarRow
|
||||
stars={votes.categories.maneuverability}
|
||||
stars={votes?.categories.maneuverability ?? null}
|
||||
children="Maneuverability"
|
||||
/>
|
||||
<StarRow
|
||||
stars={votes.categories.survivability}
|
||||
stars={votes?.categories.survivability ?? null}
|
||||
children="Survivability"
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
{wargaming && (
|
||||
<Text align="center" size="2" color="gray" mt="2">
|
||||
<Flex justify="center">
|
||||
<Text size="2" color="gray" mt="2">
|
||||
<>
|
||||
{votes.last_updated
|
||||
? `You voted on ${new Date(votes.last_updated).toLocaleDateString()}`
|
||||
: `You haven't voted yet`}
|
||||
{votes ? (
|
||||
wargaming ? (
|
||||
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>
|
||||
)}
|
||||
</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 { Button, Code, Flex } from '@radix-ui/themes';
|
||||
import { Suspense } from 'react';
|
||||
import { classIcons } from '../../../../../../../components/ClassIcon';
|
||||
import { Link } from '../../../../../../../components/Link';
|
||||
import { asset } from '../../../../../../../core/blitzkit/asset';
|
||||
@@ -9,7 +8,6 @@ import * as App from '../../../../../../../stores/app';
|
||||
import * as Duel from '../../../../../../../stores/duel';
|
||||
import { Listing } from './components/Listing';
|
||||
import { Votes } from './components/Votes';
|
||||
import { VotesSkeleton } from './components/VotesSkeleton';
|
||||
|
||||
export function MetaSection() {
|
||||
const developerMode = App.useDeferred(false, (state) => state.developerMode);
|
||||
@@ -40,7 +38,10 @@ export function MetaSection() {
|
||||
</Link>
|
||||
</Flex>
|
||||
|
||||
<Flex gap="6">
|
||||
<Flex
|
||||
gap={{ initial: '0', sm: '6' }}
|
||||
direction={{ initial: 'column', sm: 'row' }}
|
||||
>
|
||||
<Flex direction="column" width="100%">
|
||||
{protagonist.nameFull && (
|
||||
<Listing label="Full-name">{protagonist.nameFull}</Listing>
|
||||
@@ -139,9 +140,7 @@ export function MetaSection() {
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<Suspense fallback={<VotesSkeleton />}>
|
||||
<Votes />
|
||||
</Suspense>
|
||||
<Votes />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CaretLeftIcon, CaretRightIcon, PlusIcon } from '@radix-ui/react-icons';
|
||||
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 { imgur } from '../../../../../../../core/blitzkit/imgur';
|
||||
import { tankDefinitions } from '../../../../../../../core/blitzkit/tankDefinitions';
|
||||
@@ -128,6 +128,7 @@ export function TechTreeSection() {
|
||||
|
||||
return (
|
||||
<Flex
|
||||
key={multiplier}
|
||||
style={{
|
||||
background: `url(${imgur('7hDltb4')})`,
|
||||
backgroundSize: 'contain',
|
||||
@@ -141,7 +142,6 @@ export function TechTreeSection() {
|
||||
draft.xpMultiplier = multiplier;
|
||||
});
|
||||
}}
|
||||
key={multiplier}
|
||||
width="2.5rem"
|
||||
justify="center"
|
||||
align="center"
|
||||
@@ -164,15 +164,15 @@ export function TechTreeSection() {
|
||||
const tank = awaitedTankDefinitions[id];
|
||||
|
||||
return (
|
||||
<>
|
||||
{index > 0 && <Arrow />}
|
||||
<Fragment key={id}>
|
||||
{index > 0 && <Arrow key={`${id}-arrow`} />}
|
||||
<Node
|
||||
key={id}
|
||||
key={`${id}-node`}
|
||||
id={id}
|
||||
nextIds={last ? tank.successors : [line[index + 1]]}
|
||||
highlight={last}
|
||||
/>
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -180,14 +180,14 @@ export function TechTreeSection() {
|
||||
<>
|
||||
<Arrow />
|
||||
{master.successors!.map((id, index) => (
|
||||
<>
|
||||
<Fragment key={id}>
|
||||
{index > 0 && (
|
||||
<Text color="gray">
|
||||
<Text color="gray" key={`${id}-plus`}>
|
||||
<PlusIcon />
|
||||
</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 { unwrapBlitzkitResponse } from '../core/blitzkit/unwrapBlitzkitResponse';
|
||||
import * as App from '../stores/app';
|
||||
@@ -20,32 +20,25 @@ const cache: Record<string, Promise<TankVotes> | TankVotes> = {};
|
||||
|
||||
export function useTankVotes(id: number) {
|
||||
const wargaming = App.use((state) => state.logins.wargaming);
|
||||
const promise = useMemo(
|
||||
() =>
|
||||
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);
|
||||
const [votes, setVotes] = useState<TankVotes | null>(null);
|
||||
|
||||
cache[id] = cacheable;
|
||||
}
|
||||
}),
|
||||
[id, wargaming],
|
||||
);
|
||||
const votes = use(promise);
|
||||
useEffect(() => {
|
||||
if (id in cache) {
|
||||
Promise.all([cache[id]]).then(([votes]) => setVotes(votes));
|
||||
} else {
|
||||
const cacheable = fetch(
|
||||
`/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;
|
||||
}
|
||||
Reference in new issue
Block a user