feat: username display

This commit is contained in:
TrèsAbhi committed 2023-10-31 15:20:37 -05:00
1 parent 04ce20857e
commit d7dfbaa799
3 files changed
+133 -42

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
import { style } from '@vanilla-extract/css';
export const noArrows = style({
'::-webkit-progress-inner-value': {
appearance: 'none',
display: 'none',
margin: 0,
},
});
+124 -18
View File
@@ -9,53 +9,104 @@ import {
Text, Text,
TextField, TextField,
} from '@radix-ui/themes'; } from '@radix-ui/themes';
import { produce } from 'immer';
import { range } from 'lodash'; import { range } from 'lodash';
import { useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import useSWR from 'swr'; import useSWR from 'swr';
import { create } from 'zustand';
import * as Leaderboard from '../../../components/Leaderboard'; import * as Leaderboard from '../../../components/Leaderboard';
import PageWrapper from '../../../components/PageWrapper'; import PageWrapper from '../../../components/PageWrapper';
import { REGIONS, REGION_NAMES, Region } from '../../../constants/regions'; import { REGIONS, REGION_NAMES, Region } from '../../../constants/regions';
import { getAccountInfo } from '../../../core/blitz/getAccountInfo';
import getRatingsInfo from '../../../core/blitz/getRatingsInfo'; import getRatingsInfo from '../../../core/blitz/getRatingsInfo';
import getArchivedRatingsInfoAPI from '../../../core/blitzkrieg/getArchivedRatingsInfoAPI'; import getArchivedRatingsInfoAPI from '../../../core/blitzkrieg/getArchivedRatingsInfoAPI';
import { getArchivedRatingsLeaderboardAPI } from '../../../core/blitzkrieg/getArchivedRatingsLeaderboardAPI'; import { getArchivedRatingsLeaderboardAPI } from '../../../core/blitzkrieg/getArchivedRatingsLeaderboardAPI';
import { numberFetcher } from '../../../core/blitzkrieg/numberFetcher'; import { numberFetcher } from '../../../core/blitzkrieg/numberFetcher';
import { useRatings } from '../../../stores/ratings'; import { noArrows } from './page.css';
export const FIRST_ARCHIVED_RATINGS_SEASON = 49; export const FIRST_ARCHIVED_RATINGS_SEASON = 49;
const ROWS_PER_PAGE = 64;
const useUsernameCache = create<Record<Region, Record<number, string>>>(() => ({
asia: {},
com: {},
eu: {},
}));
const clanCache: Record<Region, Record<number, string>> = {
com: {},
eu: {},
asia: {},
};
export default function Page() { export default function Page() {
const ratings = useRatings(); const usernameCache = useUsernameCache();
const [region, setRegion] = useState<Region>('com');
// null being the latest season // null being the latest season
const [season, setSeason] = useState<null | number>(null); const [season, setSeason] = useState<null | number>(null);
const { data: ratingsInfo } = useSWR( const { data: ratingsInfo } = useSWR(
`ratings-info-${ratings.region}-${season}`, `ratings-info-${region}-${season}`,
() => () =>
season === null season === null
? getRatingsInfo(ratings.region) ? getRatingsInfo(region)
: getArchivedRatingsInfoAPI(ratings.region, season), : getArchivedRatingsInfoAPI(region, season),
); );
const [jumpToLeague, setJumpToLeague] = useState(0); const [jumpToLeague, setJumpToLeague] = useState(0);
const { data: latestArchivedSeasonNumber } = useSWR<number>( const { data: latestArchivedSeasonNumber } = useSWR<number>(
'/api/ratings/latest-archived-season-number', '/api/ratings/latest-archived-season-number',
numberFetcher, numberFetcher,
); );
const players = useSWR(`ratings-players-${ratings.region}-${season}`, () => { const players = useSWR(`ratings-players-${region}-${season}`, () => {
if (season === null) { if (season === null) {
// i'll deal with this later // i'll deal with this later
return null; return null;
} else { } else {
return getArchivedRatingsLeaderboardAPI(ratings.region, season); return getArchivedRatingsLeaderboardAPI(region, season);
} }
}); });
const [page, setPage] = useState(0);
const pageInput = useRef<HTMLInputElement>(null);
const playerSlice = players.data?.slice(
page * ROWS_PER_PAGE,
page * ROWS_PER_PAGE + ROWS_PER_PAGE,
);
cachePage(page - 1);
cachePage(page);
cachePage(page + 1);
function cachePage(page: number) {
const ids = players.data
?.slice(page * ROWS_PER_PAGE, page * ROWS_PER_PAGE + ROWS_PER_PAGE)
.map(({ id }) => id)
.filter((id) => !(id in usernameCache[region]));
if (ids && ids.length > 0) {
getAccountInfo(region, ids).then((data) => {
data.map((player) => {
useUsernameCache.setState(
produce((draft) => {
draft[region][player.account_id] = player.nickname;
}),
);
});
});
}
}
useEffect(() => {
if (pageInput.current) pageInput.current.value = `${page + 1}`;
}, [page]);
useEffect(() => {
setPage(0);
}, [season, region]);
return ( return (
<PageWrapper> <PageWrapper>
<Flex gap="2" wrap="wrap"> <Flex gap="2">
<Select.Root <Select.Root
defaultValue={ratings.region} defaultValue={region}
onValueChange={(value) => onValueChange={(value) => setRegion(region)}
useRatings.setState({ region: value as Region })
}
> >
<Select.Trigger style={{ flex: 1 }} /> <Select.Trigger style={{ flex: 1 }} />
@@ -98,7 +149,10 @@ export default function Page() {
</Select.Group> </Select.Group>
</Select.Content> </Select.Content>
</Select.Root> </Select.Root>
</Flex>
<Flex justify="between">
<Flex gap="2">
<Popover.Root> <Popover.Root>
<Popover.Trigger> <Popover.Trigger>
<Button variant="soft">Jump to league</Button> <Button variant="soft">Jump to league</Button>
@@ -163,19 +217,71 @@ export default function Page() {
</Popover.Content> </Popover.Content>
</Popover.Root> </Popover.Root>
</Flex> </Flex>
<Flex gap="2" align="center">
<Button
variant="soft"
onClick={() => setPage((page) => Math.max(page - 1, 0))}
>
{'<'}
</Button>
<TextField.Root className={noArrows}>
<TextField.Slot>Page</TextField.Slot>
<TextField.Input
className={noArrows}
type="number"
ref={pageInput}
style={{ width: 64, textAlign: 'center' }}
onBlur={(event) => {
setPage(
Math.max(
0,
Math.min(
Math.floor((players.data?.length ?? 0) / ROWS_PER_PAGE),
event.target.valueAsNumber - 1,
),
),
);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
(event.target as HTMLInputElement).blur();
}
}}
/>
<TextField.Slot>
out of {Math.ceil((players.data?.length ?? 0) / ROWS_PER_PAGE)}
</TextField.Slot>
</TextField.Root>
<Button
variant="soft"
onClick={() =>
setPage((page) =>
Math.min(
page + 1,
Math.floor((players.data?.length ?? 0) / ROWS_PER_PAGE),
),
)
}
>
{'>'}
</Button>
</Flex>
</Flex>
{players.data && (
<Leaderboard.Root> <Leaderboard.Root>
{players.data.map((player, index) => ( {playerSlice?.map((player, index) => (
<Leaderboard.Item <Leaderboard.Item
nickname={`${player.id}`} nickname={
position={index + 1} usernameCache[region][player.id] ?? `Unknown player ${player.id}`
}
position={page * ROWS_PER_PAGE + index + 1}
score={player.score} score={player.score}
key={player.id} key={player.id}
/> />
))} ))}
</Leaderboard.Root> </Leaderboard.Root>
)}
{players.isLoading && <Text>Loading...</Text>} {players.isLoading && <Text>Loading...</Text>}
</PageWrapper> </PageWrapper>
-24
View File
@@ -1,24 +0,0 @@
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
import { Region } from '../constants/regions';
interface Ratings {
/**
* When null, the current season is used unless there ain't one in which case
* we do whatever lol
*/
region: Region;
}
export const useRatings = create<Ratings>()(
devtools(
persist(
(set) => ({
region: 'com',
}),
{
name: 'ratings',
},
),
),
);