feat: career stats

This commit is contained in:
TrèsAbhi committed 2023-09-24 17:02:09 -05:00
1 parent 8c041d4eef
commit d801447e5d
6 files changed
+156 -25

No files matched your search

+150 -19
View File
@@ -8,7 +8,7 @@ import { Button } from '../../../components/Button';
import PageWrapper from '../../../components/PageWrapper'; import PageWrapper from '../../../components/PageWrapper';
import { SearchBar } from '../../../components/SearchBar'; import { SearchBar } from '../../../components/SearchBar';
import { TreeTypeEnum } from '../../../components/Tanks'; import { TreeTypeEnum } from '../../../components/Tanks';
import { REGION_NAMES } from '../../../constants/regions'; import { REGION_NAMES, Region } from '../../../constants/regions';
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID'; import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
import calculateWN8 from '../../../core/blitz/calculateWN8'; import calculateWN8 from '../../../core/blitz/calculateWN8';
import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats'; import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats';
@@ -28,6 +28,9 @@ import {
} from '../../../types/tanksStats'; } from '../../../types/tanksStats';
import * as styles from './page.css'; import * as styles from './page.css';
// 1 once per 5 seconds
const REFRESH_RATE = 1 / 5;
export default function Page() { export default function Page() {
const input = useRef<HTMLInputElement>(null); const input = useRef<HTMLInputElement>(null);
const [searchResults, setSearchResults] = useState< const [searchResults, setSearchResults] = useState<
@@ -45,27 +48,32 @@ export default function Page() {
500, 500,
); );
const session = useSession(); 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< const [diff, setDiff] = useState<
| { | {
careerWN8: number;
list: {
stats: IndividualTankStats; stats: IndividualTankStats;
tankopedia?: TankopediaEntry; tankopedia?: TankopediaEntry;
career: IndividualTankStats; career: IndividualTankStats;
currentWN8?: number; currentWN8?: number;
careerWN8?: number; careerWN8?: number;
}[] }[];
}
| undefined | undefined
>(undefined); >(undefined);
useEffect(() => { async function recalculateDiff() {
(async () => {
if (session.isTracking) { if (session.isTracking) {
const { id, region } = session; const { id, region } = session;
const careerRaw = await getWargamingResponse<TanksStats>( const careerRaw = await getWargamingResponse<TanksStats>(
`https://api.wotblitz.${region}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, `https://api.wotblitz.${region}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
); );
const career = careerRaw[id].reduce< const career = careerRaw[id].reduce<Record<number, IndividualTankStats>>(
Record<number, IndividualTankStats>
>(
(accumulator, curr) => ({ (accumulator, curr) => ({
...accumulator, ...accumulator,
[curr.tank_id]: curr, [curr.tank_id]: curr,
@@ -74,9 +82,27 @@ export default function Page() {
); );
const awaitedTankopedia = await tankopedia; const awaitedTankopedia = await tankopedia;
const awaitedTankAverages = await tankAverages; 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( setDiff({
( careerWN8,
list: (
Object.values( Object.values(
diffNormalizedTankStats(session.tankStats, career), diffNormalizedTankStats(session.tankStats, career),
) as IndividualTankStats[] ) as IndividualTankStats[]
@@ -86,10 +112,7 @@ export default function Page() {
tankopedia: awaitedTankopedia[stats.tank_id], tankopedia: awaitedTankopedia[stats.tank_id],
career: career[stats.tank_id], career: career[stats.tank_id],
currentWN8: awaitedTankAverages[stats.tank_id] currentWN8: awaitedTankAverages[stats.tank_id]
? calculateWN8( ? calculateWN8(awaitedTankAverages[stats.tank_id].all, stats.all)
awaitedTankAverages[stats.tank_id].all,
stats.all,
)
: undefined, : undefined,
careerWN8: awaitedTankAverages[stats.tank_id] careerWN8: awaitedTankAverages[stats.tank_id]
? calculateWN8( ? calculateWN8(
@@ -99,9 +122,16 @@ export default function Page() {
: undefined, : undefined,
}; };
}), }),
); });
} }
})(); }
async function setSession(region: Region, id: number, nickname: string) {}
useEffect(() => {
recalculateDiff();
const interval = setInterval(recalculateDiff, 1000 / REFRESH_RATE);
return () => clearInterval(interval);
}, []); }, []);
return ( return (
@@ -135,7 +165,6 @@ export default function Page() {
marginTop: 8, marginTop: 8,
flexDirection: 'column', flexDirection: 'column',
backgroundColor: theme.colors.componentNonInteractive, backgroundColor: theme.colors.componentNonInteractive,
border: theme.borderStyles.nonInteractive,
borderRadius: 4, borderRadius: 4,
padding: 8, padding: 8,
boxSizing: 'border-box', boxSizing: 'border-box',
@@ -244,8 +273,110 @@ export default function Page() {
</div> </div>
<Breakdown.Root> <Breakdown.Root>
{diff && {session.isTracking && since !== undefined && diff !== undefined && (
diff <Breakdown.Row
title={`Since ${since.toLocaleString(undefined, {
hour: 'numeric',
minute: 'numeric',
hour12: true,
month: 'short',
year: 'numeric',
day: 'numeric',
})}`}
stats={[
{
title: 'Battles',
current: diff.list
.reduce(
(accumulator, { stats }) => accumulator + stats.all.battles,
0,
)
.toLocaleString(),
career: tankStatsArray
.reduce(
(accumulator, stats) => accumulator + stats.all.battles,
0,
)
.toLocaleString(),
},
{
title: 'Winrate',
current: `${(
100 *
(diff.list.reduce(
(accumulator, { stats }) => accumulator + stats.all.wins,
0,
) /
diff.list.reduce(
(accumulator, { stats }) =>
accumulator + stats.all.battles,
0,
))
).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 currentWN8 =
diff.list.reduce(
(accumulator, { currentWN8, stats }) =>
stats.all.battles * (currentWN8 ?? 0) + accumulator,
0,
) /
diff.list.reduce(
(accumulator, { currentWN8, stats }) =>
(currentWN8 === undefined ? 0 : stats.all.battles) +
accumulator,
0,
);
return {
title: 'WN8',
current: Math.round(currentWN8).toLocaleString(),
percentile: getWN8Percentile(currentWN8),
career: Math.round(diff.careerWN8).toLocaleString(),
};
})(),
{
title: 'Damage',
current: Math.round(
diff.list.reduce(
(accumulator, { stats }) =>
accumulator + stats.all.damage_dealt,
0,
) /
diff.list.reduce(
(accumulator, { stats }) =>
accumulator + stats.all.battles,
0,
),
).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) .sort((a, b) => b.stats.last_battle_time - a.stats.last_battle_time)
.map(({ stats, tankopedia, career, careerWN8, currentWN8 }) => ( .map(({ stats, tankopedia, career, careerWN8, currentWN8 }) => (
<Breakdown.Row <Breakdown.Row
@@ -257,7 +388,7 @@ export default function Page() {
if (tankopedia?.is_collectible) return TreeTypeEnum.Collector; if (tankopedia?.is_collectible) return TreeTypeEnum.Collector;
if (tankopedia?.is_premium) return TreeTypeEnum.Premium; if (tankopedia?.is_premium) return TreeTypeEnum.Premium;
})()} })()}
rows={[ stats={[
{ {
title: 'Battles', title: 'Battles',
current: stats.all.battles.toLocaleString(), current: stats.all.battles.toLocaleString(),
+3 -3
View File
@@ -5,13 +5,13 @@ import { TREE_TYPE_ICONS, TreeTypeEnum } from '../../Tanks';
import { RowStat } from './RowStat'; import { RowStat } from './RowStat';
interface RowProps { interface RowProps {
type: 'tank' | 'summary'; type?: 'tank' | 'summary';
title: string; title: string;
minimized?: boolean; minimized?: boolean;
treeType?: TreeTypeEnum; treeType?: TreeTypeEnum;
tankType?: TankType; tankType?: TankType;
rows: { stats: {
title: string; title: string;
current?: number | string; current?: number | string;
career?: number | string; career?: number | string;
@@ -77,7 +77,7 @@ export function Row(props: RowProps) {
paddingBottom: 8, paddingBottom: 8,
}} }}
> >
{props.rows.map((row, index) => ( {props.stats.map((row, index) => (
<RowStat <RowStat
minimized={props.minimized} minimized={props.minimized}
key={index} key={index}
+1 -1
View File
@@ -23,7 +23,7 @@ export function Button({ color, style, ...props }: ButtonProps) {
backgroundColor: backgroundColor:
theme.colors[`componentInteractive${BUTTON_COLORS[color]}`], theme.colors[`componentInteractive${BUTTON_COLORS[color]}`],
borderRadius: 4, borderRadius: 4,
border: theme.borderStyles[`interactive${BUTTON_COLORS[color]}`], border: 'none',
cursor: 'pointer', cursor: 'pointer',
padding: 8, padding: 8,
display: 'flex', display: 'flex',
-1
View File
@@ -14,7 +14,6 @@ export const SearchBar = forwardRef<HTMLInputElement, ComponentProps<'input'>>(
display: 'flex', display: 'flex',
padding: 8, padding: 8,
borderRadius: 4, borderRadius: 4,
border: theme.borderStyles.interactive,
backgroundColor: theme.colors.componentInteractive, backgroundColor: theme.colors.componentInteractive,
color: theme.colors.textLowContrast, color: theme.colors.textLowContrast,
fontSize: 16, fontSize: 16,
+1 -1
View File
@@ -138,7 +138,7 @@ export default async function today(
treeType={tankopediaEntry ? await getTreeType(id) : undefined} treeType={tankopediaEntry ? await getTreeType(id) : undefined}
title={showTotal && id === 0 ? 'Total' : await resolveTankName(id)} title={showTotal && id === 0 ? 'Total' : await resolveTankName(id)}
minimized={showTotal ? index > maximized : index + 1 > maximized} minimized={showTotal ? index > maximized : index + 1 > maximized}
rows={[ stats={[
{ {
title: 'Battles', title: 'Battles',
current: tankStats.battles.toLocaleString(), current: tankStats.battles.toLocaleString(),
+1
View File
@@ -14,6 +14,7 @@ type Session =
id: number; id: number;
nickname: string; nickname: string;
tankStats: NormalizedTankStats; tankStats: NormalizedTankStats;
time: number;
}; };
export const useSession = create<Session>()( export const useSession = create<Session>()(