diff --git a/bun.lockb b/bun.lockb index 972b90a46..1e02b7c81 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/website/package.json b/packages/website/package.json index 4794fc8d9..30d60d383 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -14,6 +14,7 @@ "@nanostores/persistent": "^0.10.2", "@nanostores/react": "^0.7.3", "@nivo/bar": "^0.87.0", + "@nivo/line": "^0.88.0", "@radix-ui/colors": "^3.0.0", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^3.1.4", diff --git a/packages/website/src/components/Charts/constants.ts b/packages/website/src/components/Charts/constants.ts new file mode 100644 index 000000000..c5dc4f7c2 --- /dev/null +++ b/packages/website/src/components/Charts/constants.ts @@ -0,0 +1 @@ +export const FIRST_AVERAGES_ENTRY = 19925; diff --git a/packages/website/src/components/Nivo/ThemedLine.tsx b/packages/website/src/components/Nivo/ThemedLine.tsx new file mode 100644 index 000000000..58614bcdc --- /dev/null +++ b/packages/website/src/components/Nivo/ThemedLine.tsx @@ -0,0 +1,15 @@ +import { ResponsiveLine, type LineSvgProps } from '@nivo/line'; +import { nivoTheme } from '../../core/nivo/theme'; + +export function ThemedLine(props: LineSvgProps) { + return ( + + ); +} diff --git a/packages/website/src/components/Tankopedia/TankopediaCharts.tsx b/packages/website/src/components/Tankopedia/TankopediaCharts.tsx new file mode 100644 index 000000000..09917090c --- /dev/null +++ b/packages/website/src/components/Tankopedia/TankopediaCharts.tsx @@ -0,0 +1,63 @@ +import { asset, AverageDefinitions } from '@blitzkit/core'; +import { Box, Flex, Heading, Text } from '@radix-ui/themes'; +import { range } from 'lodash-es'; +import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio'; +import { Duel } from '../../stores/duel'; +import { FIRST_AVERAGES_ENTRY } from '../Charts/constants'; +import { ThemedLine } from '../Nivo/ThemedLine'; + +const currentEntry = Math.floor(Date.now() / 1000 / 60 / 60 / 24); +const days = await Promise.all( + range(FIRST_AVERAGES_ENTRY, currentEntry).map(async (day) => { + const url = asset(`averages/${day}.pb`); + const response = await fetch(url); + + if (response.status !== 200) return null; + + const buffer = await response.arrayBuffer(); + const averageDefinitions = AverageDefinitions.decode( + new Uint8Array(buffer), + ); + + return { day, definitions: averageDefinitions }; + }), +).then((days) => days.filter((day) => day !== null)); + +export function TankopediaCharts() { + const id = Duel.use((state) => state.protagonist.tank.id); + const ratio = useAveragesExclusionRatio(); + const data = days.map(({ day, definitions }) => { + const entry = definitions.averages[id]; + const x = new Date(day * 24 * 60 * 60 * 1000); + const y = entry ? Math.round((entry.samples.d_7 / 7) * ratio) : 0; + + return { x, y }; + }); + + return ( + + + Players + + 7-day rolling average + + + + { + return (date as Date).toLocaleDateString(undefined, { + day: 'numeric', + month: 'short', + year: 'numeric', + }); + }} + xScale={{ type: 'time' }} + /> + + + + ); +} diff --git a/packages/website/src/pages/tools/tankopedia/[id]/_index.tsx b/packages/website/src/pages/tools/tankopedia/[id]/_index.tsx index 6d0a1c997..ed1ca3be8 100644 --- a/packages/website/src/pages/tools/tankopedia/[id]/_index.tsx +++ b/packages/website/src/pages/tools/tankopedia/[id]/_index.tsx @@ -6,6 +6,7 @@ import { GameModeSection } from '../../../../components/Tankopedia/GameModeSecti import { HeroSection } from '../../../../components/Tankopedia/HeroSection'; import { HistorySection } from '../../../../components/Tankopedia/HistorySection'; import { MetaSection } from '../../../../components/Tankopedia/MetaSection'; +import { TankopediaCharts } from '../../../../components/Tankopedia/TankopediaCharts'; import { TechTreeSection } from '../../../../components/Tankopedia/TechTreeSection'; import { VideoSection } from '../../../../components/Tankopedia/VideoSection'; import { awaitableModelDefinitions } from '../../../../core/awaitables/modelDefinitions'; @@ -48,6 +49,7 @@ export function Page({ id, skeleton }: PageProps) { )} +