mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: session tracker list
This commit is contained in:
1 parent
6ff2a23ff7
commit
8c041d4eef
6 files changed
+145
-29
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { tankAverages } from '../../../core/blitzstars/tankAverages';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
return NextResponse.json(await tankAverages);
|
||||||
|
}
|
||||||
+106
-10
@@ -7,14 +7,18 @@ import * as Breakdown from '../../../components/Breakdown';
|
|||||||
import { Button } from '../../../components/Button';
|
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 { REGION_NAMES } from '../../../constants/regions';
|
import { REGION_NAMES } from '../../../constants/regions';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
|
||||||
|
import calculateWN8 from '../../../core/blitz/calculateWN8';
|
||||||
import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats';
|
import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats';
|
||||||
|
import getWN8Percentile from '../../../core/blitz/getWN8Percentile';
|
||||||
import getWargamingResponse from '../../../core/blitz/getWargamingResponse';
|
import getWargamingResponse from '../../../core/blitz/getWargamingResponse';
|
||||||
import listPlayers, {
|
import listPlayers, {
|
||||||
AccountListWithServer,
|
AccountListWithServer,
|
||||||
} from '../../../core/blitz/listPlayers';
|
} from '../../../core/blitz/listPlayers';
|
||||||
import { tankopedia } from '../../../core/blitz/tankopedia';
|
import { TankopediaEntry, tankopedia } from '../../../core/blitz/tankopedia';
|
||||||
|
import { tankAverages } from '../../../core/blitzstars/tankAverages';
|
||||||
import { theme } from '../../../stitches.config';
|
import { theme } from '../../../stitches.config';
|
||||||
import { useSession } from '../../../stores/session';
|
import { useSession } from '../../../stores/session';
|
||||||
import {
|
import {
|
||||||
@@ -24,8 +28,6 @@ import {
|
|||||||
} from '../../../types/tanksStats';
|
} from '../../../types/tanksStats';
|
||||||
import * as styles from './page.css';
|
import * as styles from './page.css';
|
||||||
|
|
||||||
(async () => console.log(await tankopedia))();
|
|
||||||
|
|
||||||
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<
|
||||||
@@ -43,16 +45,25 @@ export default function Page() {
|
|||||||
500,
|
500,
|
||||||
);
|
);
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const [diff, setDiff] = useState<NormalizedTankStats | undefined>(undefined);
|
const [diff, setDiff] = useState<
|
||||||
|
| {
|
||||||
|
stats: IndividualTankStats;
|
||||||
|
tankopedia?: TankopediaEntry;
|
||||||
|
career: IndividualTankStats;
|
||||||
|
currentWN8?: number;
|
||||||
|
careerWN8?: number;
|
||||||
|
}[]
|
||||||
|
| undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (session.isTracking) {
|
if (session.isTracking) {
|
||||||
const { id, region } = session;
|
const { id, region } = session;
|
||||||
const currentRaw = 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 current = currentRaw[id].reduce<
|
const career = careerRaw[id].reduce<
|
||||||
Record<number, IndividualTankStats>
|
Record<number, IndividualTankStats>
|
||||||
>(
|
>(
|
||||||
(accumulator, curr) => ({
|
(accumulator, curr) => ({
|
||||||
@@ -61,8 +72,34 @@ export default function Page() {
|
|||||||
}),
|
}),
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
const awaitedTankopedia = await tankopedia;
|
||||||
|
const awaitedTankAverages = await tankAverages;
|
||||||
|
|
||||||
setDiff(diffNormalizedTankStats(session.tankStats, current));
|
setDiff(
|
||||||
|
(
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -208,9 +245,68 @@ export default function Page() {
|
|||||||
|
|
||||||
<Breakdown.Root>
|
<Breakdown.Root>
|
||||||
{diff &&
|
{diff &&
|
||||||
(Object.entries(diff) as [string, IndividualTankStats][])
|
diff
|
||||||
.sort(([, a], [, b]) => b.last_battle_time - a.last_battle_time)
|
.sort((a, b) => b.stats.last_battle_time - a.stats.last_battle_time)
|
||||||
.map(([id, tankStats]) => <Breakdown.Row key={id} rows={[]} />)}
|
.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;
|
||||||
|
})()}
|
||||||
|
rows={[
|
||||||
|
{
|
||||||
|
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>
|
</Breakdown.Root>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -41,14 +41,16 @@ export function Row(props: RowProps) {
|
|||||||
backgroundColor: theme.colors.componentInteractive,
|
backgroundColor: theme.colors.componentInteractive,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.type === 'tank' &&
|
{props.type === 'tank' && props.tankType !== undefined && (
|
||||||
props.treeType !== undefined &&
|
<img
|
||||||
props.tankType !== undefined && (
|
src={
|
||||||
<img
|
TREE_TYPE_ICONS[props.treeType ?? TreeTypeEnum.TechTree][
|
||||||
src={TREE_TYPE_ICONS[props.treeType][props.tankType]}
|
props.tankType
|
||||||
style={{ width: 16, height: 16 }}
|
]
|
||||||
/>
|
}
|
||||||
)}
|
style={{ width: 16, height: 16 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { deburr } from 'lodash';
|
import { deburr } from 'lodash';
|
||||||
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
import { WARGAMING_APPLICATION_ID } from '../../constants/wargamingApplicationID';
|
||||||
|
import { context } from '../blitzkrieg/context';
|
||||||
import getWargamingResponse from './getWargamingResponse';
|
import getWargamingResponse from './getWargamingResponse';
|
||||||
|
|
||||||
export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank';
|
export type TankType = 'AT-SPG' | 'lightTank' | 'mediumTank' | 'heavyTank';
|
||||||
@@ -23,14 +24,16 @@ export interface Tankopedia {
|
|||||||
|
|
||||||
console.log('Caching tankopedia...');
|
console.log('Caching tankopedia...');
|
||||||
export const tankopedia = fetch(
|
export const tankopedia = fetch(
|
||||||
typeof window === 'undefined'
|
context === 'bot'
|
||||||
? 'https://www.blitzstars.com/bs-tankopedia.json'
|
? 'https://www.blitzstars.com/bs-tankopedia.json'
|
||||||
: '/api/tankopedia',
|
: '/api/tankopedia',
|
||||||
)
|
)
|
||||||
.then(async (response) => response.json())
|
.then(async (response) => response.json())
|
||||||
.then((wrapperTankopedia) => {
|
.then((wrapperTankopedia) => {
|
||||||
console.log('Tankopedia cached');
|
console.log('Tankopedia cached');
|
||||||
return (wrapperTankopedia as { data: Tankopedia }).data;
|
return context === 'bot'
|
||||||
|
? (wrapperTankopedia as { data: Tankopedia }).data
|
||||||
|
: (wrapperTankopedia as Tankopedia);
|
||||||
});
|
});
|
||||||
const entries = new Promise<TankopediaEntry[]>(async (resolve) => {
|
const entries = new Promise<TankopediaEntry[]>(async (resolve) => {
|
||||||
resolve(Object.entries(await tankopedia).map(([, entry]) => entry));
|
resolve(Object.entries(await tankopedia).map(([, entry]) => entry));
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
type Context = 'website' | 'bot';
|
||||||
|
|
||||||
|
export const context: Context =
|
||||||
|
typeof window === 'undefined' ? 'bot' : 'website';
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { AllStats } from '../../types/accountInfo';
|
import { AllStats } from '../../types/accountInfo';
|
||||||
|
import { context } from '../blitzkrieg/context';
|
||||||
|
|
||||||
export interface SpecialStats {
|
export interface SpecialStats {
|
||||||
winrate: number;
|
winrate: number;
|
||||||
@@ -82,20 +83,24 @@ export type TankAverages = Record<number, IndividualTankAverage>;
|
|||||||
|
|
||||||
console.log('Caching tank averages...');
|
console.log('Caching tank averages...');
|
||||||
export const tankAverages = fetch(
|
export const tankAverages = fetch(
|
||||||
'https://www.blitzstars.com/api/tankaverages.json',
|
context === 'bot'
|
||||||
|
? 'https://www.blitzstars.com/api/tankaverages.json'
|
||||||
|
: '/api/tank-averages',
|
||||||
)
|
)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((individualTankAverages) => {
|
.then((json) => {
|
||||||
const partialTankAverages: TankAverages = {};
|
if (context === 'bot') {
|
||||||
|
const partialTankAverages: TankAverages = {};
|
||||||
|
|
||||||
(individualTankAverages as IndividualTankAverage[]).forEach(
|
(json as IndividualTankAverage[]).forEach((individualTankAverage) => {
|
||||||
(individualTankAverage) => {
|
|
||||||
partialTankAverages[individualTankAverage.tank_id] =
|
partialTankAverages[individualTankAverage.tank_id] =
|
||||||
individualTankAverage;
|
individualTankAverage;
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
console.log('Tank averages cached');
|
console.log('Tank averages cached');
|
||||||
|
|
||||||
return partialTankAverages;
|
return partialTankAverages;
|
||||||
|
} else {
|
||||||
|
return json as TankAverages;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user