mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc9ab2067d | ||
|
|
e8e1f7bafb | ||
|
|
e50a456025 | ||
|
|
d442bbca1a |
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
# Blitzkrieg 1.13.3
|
||||||
|
|
||||||
|
Streamer's delight!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Added config context menu for the session tracker embed
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "blitzkrieg",
|
"name": "blitzkrieg",
|
||||||
"version": "1.13.2",
|
"version": "1.13.3",
|
||||||
"description": "🎉 Tools for everything World of Tanks Blitz",
|
"description": "🎉 Tools for everything World of Tanks Blitz",
|
||||||
"main": "src/server.ts",
|
"main": "src/server.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+125
-108
@@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { ContextMenu } from '@radix-ui/themes';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import * as Breakdown from '../../../components/Breakdown';
|
import * as Breakdown from '../../../components/Breakdown';
|
||||||
import { TreeTypeEnum } from '../../../components/Tanks';
|
import { TreeTypeEnum } from '../../../components/Tanks';
|
||||||
@@ -13,6 +14,7 @@ import { tankAverages } from '../../../core/blitzstars/tankAverages';
|
|||||||
import { useSession } from '../../../stores/session';
|
import { useSession } from '../../../stores/session';
|
||||||
import { IndividualTankStats, TanksStats } from '../../../types/tanksStats';
|
import { IndividualTankStats, TanksStats } from '../../../types/tanksStats';
|
||||||
import { CustomColumnDisplay } from '../../tools/session/components/CustomColumn';
|
import { CustomColumnDisplay } from '../../tools/session/components/CustomColumn';
|
||||||
|
import { Menu } from '../../tools/session/components/Menu';
|
||||||
|
|
||||||
const REFRESH_RATE = 1 / 5;
|
const REFRESH_RATE = 1 / 5;
|
||||||
|
|
||||||
@@ -193,118 +195,133 @@ export default function SessionPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Breakdown.Root>
|
<ContextMenu.Root>
|
||||||
{session.showTotal &&
|
<ContextMenu.Trigger>
|
||||||
session.isTracking &&
|
<div>
|
||||||
since !== undefined &&
|
<Breakdown.Root>
|
||||||
diff !== undefined && (
|
{session.showTotal &&
|
||||||
<Breakdown.Row
|
session.isTracking &&
|
||||||
minimized={!session.showCareer}
|
since !== undefined &&
|
||||||
title={`Since ${since.toLocaleString(undefined, {
|
diff !== undefined && (
|
||||||
hour: 'numeric',
|
<Breakdown.Row
|
||||||
minute: 'numeric',
|
minimized={!session.showCareer}
|
||||||
hour12: true,
|
title={`Since ${since.toLocaleString(undefined, {
|
||||||
month: 'short',
|
hour: 'numeric',
|
||||||
year: 'numeric',
|
minute: 'numeric',
|
||||||
day: 'numeric',
|
hour12: true,
|
||||||
})}`}
|
month: 'short',
|
||||||
stats={session.customColumns.map((customColumn) => {
|
year: 'numeric',
|
||||||
const rowStat = totalRowStats[customColumn.display];
|
day: 'numeric',
|
||||||
|
})}`}
|
||||||
|
stats={session.customColumns.map((customColumn) => {
|
||||||
|
const rowStat = totalRowStats[customColumn.display];
|
||||||
|
|
||||||
if (!rowStat) return undefined;
|
if (!rowStat) return undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...rowStat,
|
...rowStat,
|
||||||
delta:
|
delta:
|
||||||
customColumn.showDelta && rowStat.percentile === undefined
|
customColumn.showDelta &&
|
||||||
? rowStat.delta
|
rowStat.percentile === undefined
|
||||||
: undefined,
|
? rowStat.delta
|
||||||
};
|
: undefined,
|
||||||
})}
|
};
|
||||||
/>
|
})}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{diff?.list
|
{diff?.list
|
||||||
.sort((a, b) => b.stats.last_battle_time - a.stats.last_battle_time)
|
.sort(
|
||||||
.map(({ stats, tankopedia, career, careerWN8, currentWN8 }) => {
|
(a, b) => b.stats.last_battle_time - a.stats.last_battle_time,
|
||||||
const rowStats: Record<
|
)
|
||||||
CustomColumnDisplay,
|
.map(({ stats, tankopedia, career, careerWN8, currentWN8 }) => {
|
||||||
Breakdown.RowStatItem | undefined
|
const rowStats: Record<
|
||||||
> = {
|
CustomColumnDisplay,
|
||||||
battles: {
|
Breakdown.RowStatItem | undefined
|
||||||
title: 'Battles',
|
> = {
|
||||||
current: stats.all.battles.toLocaleString(),
|
battles: {
|
||||||
career: career.all.battles.toLocaleString(),
|
title: 'Battles',
|
||||||
},
|
current: stats.all.battles.toLocaleString(),
|
||||||
winrate: {
|
career: career.all.battles.toLocaleString(),
|
||||||
title: 'Winrate',
|
},
|
||||||
current: `${(100 * (stats.all.wins / stats.all.battles)).toFixed(
|
winrate: {
|
||||||
2,
|
title: 'Winrate',
|
||||||
)}%`,
|
current: `${(
|
||||||
career: `${(100 * (career.all.wins / career.all.battles)).toFixed(
|
100 *
|
||||||
2,
|
(stats.all.wins / stats.all.battles)
|
||||||
)}%`,
|
).toFixed(2)}%`,
|
||||||
delta:
|
career: `${(
|
||||||
stats.all.wins / stats.all.battles -
|
100 *
|
||||||
career.all.wins / career.all.battles,
|
(career.all.wins / career.all.battles)
|
||||||
},
|
).toFixed(2)}%`,
|
||||||
wn8: {
|
delta:
|
||||||
title: 'WN8',
|
stats.all.wins / stats.all.battles -
|
||||||
current:
|
career.all.wins / career.all.battles,
|
||||||
currentWN8 === undefined
|
},
|
||||||
? undefined
|
wn8: {
|
||||||
: Math.round(currentWN8).toLocaleString(),
|
title: 'WN8',
|
||||||
career:
|
current:
|
||||||
careerWN8 === undefined
|
currentWN8 === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: Math.round(careerWN8).toLocaleString(),
|
: Math.round(currentWN8).toLocaleString(),
|
||||||
percentile:
|
career:
|
||||||
currentWN8 === undefined
|
careerWN8 === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: getWN8Percentile(currentWN8),
|
: Math.round(careerWN8).toLocaleString(),
|
||||||
},
|
percentile:
|
||||||
damage: {
|
currentWN8 === undefined
|
||||||
title: 'Damage',
|
? undefined
|
||||||
current: Math.round(
|
: getWN8Percentile(currentWN8),
|
||||||
stats.all.damage_dealt / stats.all.battles,
|
},
|
||||||
).toLocaleString(),
|
damage: {
|
||||||
career: Math.round(
|
title: 'Damage',
|
||||||
career.all.damage_dealt / career.all.battles,
|
current: Math.round(
|
||||||
).toLocaleString(),
|
stats.all.damage_dealt / stats.all.battles,
|
||||||
delta:
|
).toLocaleString(),
|
||||||
stats.all.damage_dealt / stats.all.battles -
|
career: Math.round(
|
||||||
career.all.damage_dealt / career.all.battles,
|
career.all.damage_dealt / career.all.battles,
|
||||||
},
|
).toLocaleString(),
|
||||||
none: undefined,
|
delta:
|
||||||
};
|
stats.all.damage_dealt / stats.all.battles -
|
||||||
|
career.all.damage_dealt / career.all.battles,
|
||||||
return (
|
},
|
||||||
<Breakdown.Row
|
none: undefined,
|
||||||
key={stats.tank_id}
|
|
||||||
minimized={!session.showCareer}
|
|
||||||
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={session.customColumns.map((customColumn) => {
|
|
||||||
const rowStat = rowStats[customColumn.display];
|
|
||||||
|
|
||||||
if (!rowStat) return undefined;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...rowStat,
|
|
||||||
delta:
|
|
||||||
customColumn.showDelta && rowStat.percentile === undefined
|
|
||||||
? rowStat.delta
|
|
||||||
: undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Breakdown.Row
|
||||||
|
key={stats.tank_id}
|
||||||
|
minimized={!session.showCareer}
|
||||||
|
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={session.customColumns.map((customColumn) => {
|
||||||
|
const rowStat = rowStats[customColumn.display];
|
||||||
|
|
||||||
|
if (!rowStat) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rowStat,
|
||||||
|
delta:
|
||||||
|
customColumn.showDelta &&
|
||||||
|
rowStat.percentile === undefined
|
||||||
|
? rowStat.delta
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
/>
|
</Breakdown.Root>
|
||||||
);
|
</div>
|
||||||
})}
|
</ContextMenu.Trigger>
|
||||||
</Breakdown.Root>
|
|
||||||
|
<Menu Builder={ContextMenu} />
|
||||||
|
</ContextMenu.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DropdownMenu } from '@radix-ui/themes';
|
import { ContextMenu, DropdownMenu } from '@radix-ui/themes';
|
||||||
import mutateSession, { useSession } from '../../../../stores/session';
|
import mutateSession, { useSession } from '../../../../stores/session';
|
||||||
|
|
||||||
export type CustomColumnDisplay =
|
export type CustomColumnDisplay =
|
||||||
@@ -11,20 +11,21 @@ export type CustomColumnDisplay =
|
|||||||
const NO_DELTA: CustomColumnDisplay[] = ['battles', 'wn8', 'none'];
|
const NO_DELTA: CustomColumnDisplay[] = ['battles', 'wn8', 'none'];
|
||||||
|
|
||||||
interface CustomColumn {
|
interface CustomColumn {
|
||||||
|
Builder: typeof DropdownMenu | typeof ContextMenu;
|
||||||
column: number;
|
column: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomColumn({ column }: CustomColumn) {
|
export function CustomColumn({ column, Builder }: CustomColumn) {
|
||||||
const customColumn = useSession((state) => state.customColumns[column]);
|
const customColumn = useSession((state) => state.customColumns[column]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu.Sub>
|
<Builder.Sub>
|
||||||
<DropdownMenu.SubTrigger>Column {column + 1}</DropdownMenu.SubTrigger>
|
<Builder.SubTrigger>Column {column + 1}</Builder.SubTrigger>
|
||||||
|
|
||||||
<DropdownMenu.SubContent>
|
<Builder.SubContent>
|
||||||
<DropdownMenu.Label>Display</DropdownMenu.Label>
|
<Builder.Label>Display</Builder.Label>
|
||||||
|
|
||||||
<DropdownMenu.RadioGroup
|
<Builder.RadioGroup
|
||||||
value={customColumn.display}
|
value={customColumn.display}
|
||||||
onValueChange={(event) =>
|
onValueChange={(event) =>
|
||||||
mutateSession((draft) => {
|
mutateSession((draft) => {
|
||||||
@@ -33,22 +34,18 @@ export function CustomColumn({ column }: CustomColumn) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<DropdownMenu.RadioItem value="battles">
|
<Builder.RadioItem value="battles">Battles</Builder.RadioItem>
|
||||||
Battles
|
<Builder.RadioItem value="winrate">Winrate</Builder.RadioItem>
|
||||||
</DropdownMenu.RadioItem>
|
<Builder.RadioItem value="wn8">WN8</Builder.RadioItem>
|
||||||
<DropdownMenu.RadioItem value="winrate">
|
<Builder.RadioItem value="damage">Damage</Builder.RadioItem>
|
||||||
Winrate
|
<Builder.RadioItem value="none">None</Builder.RadioItem>
|
||||||
</DropdownMenu.RadioItem>
|
</Builder.RadioGroup>
|
||||||
<DropdownMenu.RadioItem value="wn8">WN8</DropdownMenu.RadioItem>
|
|
||||||
<DropdownMenu.RadioItem value="damage">Damage</DropdownMenu.RadioItem>
|
|
||||||
<DropdownMenu.RadioItem value="none">None</DropdownMenu.RadioItem>
|
|
||||||
</DropdownMenu.RadioGroup>
|
|
||||||
|
|
||||||
{!NO_DELTA.includes(customColumn.display) && (
|
{!NO_DELTA.includes(customColumn.display) && (
|
||||||
<>
|
<>
|
||||||
<DropdownMenu.Separator />
|
<Builder.Separator />
|
||||||
|
|
||||||
<DropdownMenu.CheckboxItem
|
<Builder.CheckboxItem
|
||||||
checked={customColumn.showDelta}
|
checked={customColumn.showDelta}
|
||||||
onCheckedChange={(checked) =>
|
onCheckedChange={(checked) =>
|
||||||
mutateSession((draft) => {
|
mutateSession((draft) => {
|
||||||
@@ -57,10 +54,10 @@ export function CustomColumn({ column }: CustomColumn) {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
Show delta
|
Show delta
|
||||||
</DropdownMenu.CheckboxItem>
|
</Builder.CheckboxItem>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DropdownMenu.SubContent>
|
</Builder.SubContent>
|
||||||
</DropdownMenu.Sub>
|
</Builder.Sub>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { ContextMenu, DropdownMenu } from '@radix-ui/themes';
|
||||||
|
import { useSession } from '../../../../stores/session';
|
||||||
|
import { CustomColumn } from './CustomColumn';
|
||||||
|
|
||||||
|
interface ContextMenuProps {
|
||||||
|
Builder: typeof DropdownMenu | typeof ContextMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Menu({ Builder }: ContextMenuProps) {
|
||||||
|
const session = useSession();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Builder.Content>
|
||||||
|
<CustomColumn Builder={Builder} column={0} />
|
||||||
|
<CustomColumn Builder={Builder} column={1} />
|
||||||
|
<CustomColumn Builder={Builder} column={2} />
|
||||||
|
<CustomColumn Builder={Builder} column={3} />
|
||||||
|
|
||||||
|
<Builder.Separator />
|
||||||
|
|
||||||
|
<Builder.CheckboxItem
|
||||||
|
checked={session.showTotal}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
useSession.setState({ showTotal: checked })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Show total
|
||||||
|
</Builder.CheckboxItem>
|
||||||
|
<Builder.CheckboxItem
|
||||||
|
checked={session.showCareer}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
useSession.setState({ showCareer: checked })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Show career
|
||||||
|
</Builder.CheckboxItem>
|
||||||
|
</Builder.Content>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ import listPlayers, {
|
|||||||
import { useSession } from '../../../stores/session';
|
import { useSession } from '../../../stores/session';
|
||||||
import { NormalizedTankStats, TanksStats } from '../../../types/tanksStats';
|
import { NormalizedTankStats, TanksStats } from '../../../types/tanksStats';
|
||||||
import SessionPage from '../../embeds/session/page';
|
import SessionPage from '../../embeds/session/page';
|
||||||
import { CustomColumn } from './components/CustomColumn';
|
import { Menu } from './components/Menu';
|
||||||
import * as styles from './page.css';
|
import * as styles from './page.css';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
@@ -231,31 +231,7 @@ export default function Page() {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Content>
|
<Menu Builder={DropdownMenu} />
|
||||||
<CustomColumn column={0} />
|
|
||||||
<CustomColumn column={1} />
|
|
||||||
<CustomColumn column={2} />
|
|
||||||
<CustomColumn column={3} />
|
|
||||||
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
|
|
||||||
<DropdownMenu.CheckboxItem
|
|
||||||
checked={session.showTotal}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
useSession.setState({ showTotal: checked })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Show total
|
|
||||||
</DropdownMenu.CheckboxItem>
|
|
||||||
<DropdownMenu.CheckboxItem
|
|
||||||
checked={session.showCareer}
|
|
||||||
onCheckedChange={(checked) =>
|
|
||||||
useSession.setState({ showCareer: checked })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Show career
|
|
||||||
</DropdownMenu.CheckboxItem>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user