feat: embeds

This commit is contained in:
TrèsAbhi committed 2023-09-30 22:12:48 -05:00
1 parent 1990322d6d
commit 34f782e747
3 files changed
+299 -281

No files matched your search

+278
View File
@@ -0,0 +1,278 @@
'use client';
import { useEffect, useState } from 'react';
import * as Breakdown from '../../../components/Breakdown';
import { TreeTypeEnum } from '../../../components/Tanks';
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
import calculateWN8 from '../../../core/blitz/calculateWN8';
import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats';
import getWN8Percentile from '../../../core/blitz/getWN8Percentile';
import getWargamingResponse from '../../../core/blitz/getWargamingResponse';
import { TankopediaEntry, tankopedia } from '../../../core/blitz/tankopedia';
import { tankAverages } from '../../../core/blitzstars/tankAverages';
import { useSession } from '../../../stores/session';
import { IndividualTankStats, TanksStats } from '../../../types/tanksStats';
const REFRESH_RATE = 1 / 5;
export default function SessionPage() {
const session = useSession();
const since = session.isTracking ? new Date(session.time) : undefined;
const [diff, setDiff] = useState<
| {
careerWN8: number;
list: {
stats: IndividualTankStats;
tankopedia?: TankopediaEntry;
career: IndividualTankStats;
currentWN8?: number;
careerWN8?: number;
}[];
}
| undefined
>(undefined);
const battles = diff?.list.reduce(
(accumulator, { stats }) => accumulator + stats.all.battles,
0,
);
const tankStatsArray = session.isTracking
? (Object.values(session.tankStats) as IndividualTankStats[])
: [];
useEffect(() => {
async function recalculateDiff() {
if (session.isTracking) {
const { id, region } = session;
const careerRaw = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${region}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
);
const career = careerRaw[id].reduce<
Record<number, IndividualTankStats>
>(
(accumulator, curr) => ({
...accumulator,
[curr.tank_id]: curr,
}),
{},
);
const awaitedTankopedia = await tankopedia;
const awaitedTankAverages = await tankAverages;
const careerWN8 =
careerRaw[id].reduce(
(accumulator, { tank_id, all }) =>
accumulator +
(awaitedTankAverages[tank_id] && all.battles > 0
? all.battles *
calculateWN8(awaitedTankAverages[tank_id].all, all)
: 0),
0,
) /
careerRaw[id].reduce(
(accumulator, { tank_id, all }) =>
accumulator +
(awaitedTankAverages[tank_id] && all.battles > 0
? all.battles
: 0),
0,
);
setDiff({
careerWN8,
list: (
Object.values(
diffNormalizedTankStats(session.tankStats, career),
) as IndividualTankStats[]
).map((stats) => {
return {
stats,
tankopedia: awaitedTankopedia[stats.tank_id],
career: career[stats.tank_id],
currentWN8: awaitedTankAverages[stats.tank_id]
? calculateWN8(
awaitedTankAverages[stats.tank_id].all,
stats.all,
)
: undefined,
careerWN8: awaitedTankAverages[stats.tank_id]
? calculateWN8(
awaitedTankAverages[stats.tank_id].all,
career[stats.tank_id].all,
)
: undefined,
};
}),
});
}
}
recalculateDiff();
const interval = setInterval(recalculateDiff, 1000 / REFRESH_RATE);
return () => clearInterval(interval);
}, [session]);
return (
<Breakdown.Root>
{session.isTracking && since !== undefined && diff !== undefined && (
<Breakdown.Row
title={`Since ${since.toLocaleString(undefined, {
hour: 'numeric',
minute: 'numeric',
hour12: true,
month: 'short',
year: 'numeric',
day: 'numeric',
})}`}
stats={[
{
title: 'Battles',
current: battles!.toLocaleString(),
career: tankStatsArray
.reduce(
(accumulator, stats) => accumulator + stats.all.battles,
0,
)
.toLocaleString(),
},
{
title: 'Winrate',
current: battles
? `${(
100 *
(diff.list.reduce(
(accumulator, { stats }) => accumulator + stats.all.wins,
0,
) /
battles)
).toFixed(2)}%`
: '--',
career: `${(
100 *
(tankStatsArray.reduce(
(accumulator, stats) => accumulator + stats.all.wins,
0,
) /
tankStatsArray.reduce(
(accumulator, stats) => accumulator + stats.all.battles,
0,
))
).toFixed(2)}%`,
},
(() => {
const battlesWithWN8 = diff.list.reduce(
(accumulator, { currentWN8, stats }) =>
(currentWN8 === undefined ? 0 : stats.all.battles) +
accumulator,
0,
);
const currentWN8 =
diff.list.reduce(
(accumulator, { currentWN8, stats }) =>
stats.all.battles * (currentWN8 ?? 0) + accumulator,
0,
) / battlesWithWN8;
return {
title: 'WN8',
current: battlesWithWN8
? Math.round(currentWN8).toLocaleString()
: '--',
percentile: battlesWithWN8
? getWN8Percentile(currentWN8)
: undefined,
career: Math.round(diff.careerWN8).toLocaleString(),
};
})(),
{
title: 'Damage',
current: battles
? Math.round(
diff.list.reduce(
(accumulator, { stats }) =>
accumulator + stats.all.damage_dealt,
0,
) / battles,
).toLocaleString()
: '--',
career: Math.round(
tankStatsArray.reduce(
(accumulator, stats) => accumulator + stats.all.damage_dealt,
0,
) /
tankStatsArray.reduce(
(accumulator, stats) => accumulator + stats.all.battles,
0,
),
).toLocaleString(),
},
]}
/>
)}
{diff?.list
.sort((a, b) => b.stats.last_battle_time - a.stats.last_battle_time)
.map(({ stats, tankopedia, career, careerWN8, currentWN8 }) => (
<Breakdown.Row
key={stats.tank_id}
title={tankopedia?.name ?? `Unknown tank ${stats.tank_id}`}
type="tank"
tankType={tankopedia?.type}
treeType={(() => {
if (tankopedia?.is_collectible) return TreeTypeEnum.Collector;
if (tankopedia?.is_premium) return TreeTypeEnum.Premium;
})()}
stats={[
{
title: 'Battles',
current: stats.all.battles.toLocaleString(),
career: career.all.battles.toLocaleString(),
},
{
title: 'Winrate',
current: `${(
100 *
(stats.all.wins / stats.all.battles)
).toFixed(2)}%`,
career: `${(
100 *
(career.all.wins / career.all.battles)
).toFixed(2)}%`,
delta:
stats.all.wins / stats.all.battles -
career.all.wins / career.all.battles,
},
{
title: 'WN8',
current:
currentWN8 === undefined
? undefined
: Math.round(currentWN8).toLocaleString(),
career:
careerWN8 === undefined
? undefined
: Math.round(careerWN8).toLocaleString(),
percentile:
currentWN8 === undefined
? undefined
: getWN8Percentile(currentWN8),
},
{
title: 'Damage',
current: Math.round(
stats.all.damage_dealt / stats.all.battles,
).toLocaleString(),
career: Math.round(
career.all.damage_dealt / career.all.battles,
).toLocaleString(),
delta:
stats.all.damage_dealt / stats.all.battles -
career.all.damage_dealt / career.all.battles,
},
]}
/>
))}
</Breakdown.Root>
);
}
+10 -2
View File
@@ -1,5 +1,8 @@
'use client';
import { config } from 'dotenv';
import { Roboto_Flex } from 'next/font/google';
import { usePathname } from 'next/navigation';
import { ReactNode } from 'react';
import Navbar from '../components/Navbar';
@@ -15,15 +18,20 @@ const robotoFlex = Roboto_Flex({
});
export default function RootLayout({ children }: RootLayoutProps) {
const pathname = usePathname();
const isEmbed = pathname.split('/')[1] === 'embeds';
return (
<html lang="en" className={robotoFlex.className}>
<body
style={{
margin: 0,
background: 'url(https://i.imgur.com/PhS06NJ.png)',
background: isEmbed
? 'transparent'
: 'url(https://i.imgur.com/PhS06NJ.png)',
}}
>
<Navbar />
{!isEmbed && <Navbar />}
<div
style={{
+11 -279
View File
@@ -1,35 +1,24 @@
'use client';
import { CopyIcon, ListBulletIcon, ReloadIcon } from '@radix-ui/react-icons';
import { CopyIcon, ReloadIcon } from '@radix-ui/react-icons';
import { debounce } from 'lodash';
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import * as Breakdown from '../../../components/Breakdown';
import { ChangeEvent, useRef, useState } from 'react';
import { Button } from '../../../components/Button';
import PageWrapper from '../../../components/PageWrapper';
import { SearchBar } from '../../../components/SearchBar';
import { TreeTypeEnum } from '../../../components/Tanks';
import { REGION_NAMES, Region } from '../../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
import calculateWN8 from '../../../core/blitz/calculateWN8';
import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats';
import getWN8Percentile from '../../../core/blitz/getWN8Percentile';
import getWargamingResponse from '../../../core/blitz/getWargamingResponse';
import listPlayers, {
AccountListWithServer,
} from '../../../core/blitz/listPlayers';
import { TankopediaEntry, tankopedia } from '../../../core/blitz/tankopedia';
import { tankAverages } from '../../../core/blitzstars/tankAverages';
import { theme } from '../../../stitches.config';
import { useSession } from '../../../stores/session';
import {
IndividualTankStats,
NormalizedTankStats,
TanksStats,
} from '../../../types/tanksStats';
import { NormalizedTankStats, TanksStats } from '../../../types/tanksStats';
import SessionPage from '../../embeds/session/page';
import * as styles from './page.css';
// 1 once per 5 seconds
const REFRESH_RATE = 1 / 5;
export default function Page() {
const input = useRef<HTMLInputElement>(null);
@@ -49,24 +38,6 @@ export default function Page() {
500,
);
const session = useSession();
const tankStatsArray = session.isTracking
? (Object.values(session.tankStats) as IndividualTankStats[])
: [];
const since = session.isTracking ? new Date(session.time) : undefined;
const [diff, setDiff] = useState<
| {
careerWN8: number;
list: {
stats: IndividualTankStats;
tankopedia?: TankopediaEntry;
career: IndividualTankStats;
currentWN8?: number;
careerWN8?: number;
}[];
}
| undefined
>(undefined);
async function setSession(region: Region, id: number, nickname: string) {
input.current!.value = nickname;
@@ -94,86 +65,6 @@ export default function Page() {
});
}
useEffect(() => {
async function recalculateDiff() {
// const session = useSession.getState();
if (session.isTracking) {
const { id, region } = session;
const careerRaw = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${region}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
);
const career = careerRaw[id].reduce<
Record<number, IndividualTankStats>
>(
(accumulator, curr) => ({
...accumulator,
[curr.tank_id]: curr,
}),
{},
);
const awaitedTankopedia = await tankopedia;
const awaitedTankAverages = await tankAverages;
const careerWN8 =
careerRaw[id].reduce(
(accumulator, { tank_id, all }) =>
accumulator +
(awaitedTankAverages[tank_id] && all.battles > 0
? all.battles *
calculateWN8(awaitedTankAverages[tank_id].all, all)
: 0),
0,
) /
careerRaw[id].reduce(
(accumulator, { tank_id, all }) =>
accumulator +
(awaitedTankAverages[tank_id] && all.battles > 0
? all.battles
: 0),
0,
);
setDiff({
careerWN8,
list: (
Object.values(
diffNormalizedTankStats(session.tankStats, career),
) as IndividualTankStats[]
).map((stats) => {
return {
stats,
tankopedia: awaitedTankopedia[stats.tank_id],
career: career[stats.tank_id],
currentWN8: awaitedTankAverages[stats.tank_id]
? calculateWN8(
awaitedTankAverages[stats.tank_id].all,
stats.all,
)
: undefined,
careerWN8: awaitedTankAverages[stats.tank_id]
? calculateWN8(
awaitedTankAverages[stats.tank_id].all,
career[stats.tank_id].all,
)
: undefined,
};
}),
});
}
}
recalculateDiff();
const interval = setInterval(recalculateDiff, 1000 / REFRESH_RATE);
return () => clearInterval(interval);
}, [session]);
const battles = diff?.list.reduce(
(accumulator, { stats }) => accumulator + stats.all.battles,
0,
);
return (
<PageWrapper>
<div className={styles.toolBar}>
@@ -279,177 +170,18 @@ export default function Page() {
>
<ReloadIcon /> Reset
</Button>
<Button className={styles.toolbarButton}>
<Button
className={styles.toolbarButton}
onClick={() =>
navigator.clipboard.writeText(`${location.origin}/embeds/session`)
}
>
<CopyIcon /> Embed
</Button>
<Button className={styles.toolbarButton}>
<ListBulletIcon /> Options
</Button>
</div>
</div>
<Breakdown.Root>
{session.isTracking && since !== undefined && diff !== undefined && (
<Breakdown.Row
title={`Since ${since.toLocaleString(undefined, {
hour: 'numeric',
minute: 'numeric',
hour12: true,
month: 'short',
year: 'numeric',
day: 'numeric',
})}`}
stats={[
{
title: 'Battles',
current: battles!.toLocaleString(),
career: tankStatsArray
.reduce(
(accumulator, stats) => accumulator + stats.all.battles,
0,
)
.toLocaleString(),
},
{
title: 'Winrate',
current: battles
? `${(
100 *
(diff.list.reduce(
(accumulator, { stats }) =>
accumulator + stats.all.wins,
0,
) /
battles)
).toFixed(2)}%`
: '--',
career: `${(
100 *
(tankStatsArray.reduce(
(accumulator, stats) => accumulator + stats.all.wins,
0,
) /
tankStatsArray.reduce(
(accumulator, stats) => accumulator + stats.all.battles,
0,
))
).toFixed(2)}%`,
},
(() => {
const battlesWithWN8 = diff.list.reduce(
(accumulator, { currentWN8, stats }) =>
(currentWN8 === undefined ? 0 : stats.all.battles) +
accumulator,
0,
);
const currentWN8 =
diff.list.reduce(
(accumulator, { currentWN8, stats }) =>
stats.all.battles * (currentWN8 ?? 0) + accumulator,
0,
) / battlesWithWN8;
return {
title: 'WN8',
current: battlesWithWN8
? Math.round(currentWN8).toLocaleString()
: '--',
percentile: battlesWithWN8
? getWN8Percentile(currentWN8)
: undefined,
career: Math.round(diff.careerWN8).toLocaleString(),
};
})(),
{
title: 'Damage',
current: battles
? Math.round(
diff.list.reduce(
(accumulator, { stats }) =>
accumulator + stats.all.damage_dealt,
0,
) / battles,
).toLocaleString()
: '--',
career: Math.round(
tankStatsArray.reduce(
(accumulator, stats) =>
accumulator + stats.all.damage_dealt,
0,
) /
tankStatsArray.reduce(
(accumulator, stats) => accumulator + stats.all.battles,
0,
),
).toLocaleString(),
},
]}
/>
)}
{diff?.list
.sort((a, b) => b.stats.last_battle_time - a.stats.last_battle_time)
.map(({ stats, tankopedia, career, careerWN8, currentWN8 }) => (
<Breakdown.Row
key={stats.tank_id}
title={tankopedia?.name ?? `Unknown tank ${stats.tank_id}`}
type="tank"
tankType={tankopedia?.type}
treeType={(() => {
if (tankopedia?.is_collectible) return TreeTypeEnum.Collector;
if (tankopedia?.is_premium) return TreeTypeEnum.Premium;
})()}
stats={[
{
title: 'Battles',
current: stats.all.battles.toLocaleString(),
career: career.all.battles.toLocaleString(),
},
{
title: 'Winrate',
current: `${(
100 *
(stats.all.wins / stats.all.battles)
).toFixed(2)}%`,
career: `${(
100 *
(career.all.wins / career.all.battles)
).toFixed(2)}%`,
delta:
stats.all.wins / stats.all.battles -
career.all.wins / career.all.battles,
},
{
title: 'WN8',
current:
currentWN8 === undefined
? undefined
: Math.round(currentWN8).toLocaleString(),
career:
careerWN8 === undefined
? undefined
: Math.round(careerWN8).toLocaleString(),
percentile:
currentWN8 === undefined
? undefined
: getWN8Percentile(currentWN8),
},
{
title: 'Damage',
current: Math.round(
stats.all.damage_dealt / stats.all.battles,
).toLocaleString(),
career: Math.round(
career.all.damage_dealt / career.all.battles,
).toLocaleString(),
delta:
stats.all.damage_dealt / stats.all.battles -
career.all.damage_dealt / career.all.battles,
},
]}
/>
))}
</Breakdown.Root>
<SessionPage />
</PageWrapper>
);
}