Compare commits

...
18 Commits
Author SHA1 Message Date
TrèsAbhi 92e35bd0ac feat: 1.13.2 2023-10-05 11:04:46 -05:00
TrèsAbhi cb9d283f62 Merge pull request #39 from tresabhi/1.13.1 2023-10-01 19:03:54 -05:00
TrèsAbhi 8aba19e05b fix: duplicate name 2023-10-01 19:02:47 -05:00
TrèsAbhi b7b4035492 feat: show career options 2023-10-01 19:00:05 -05:00
TrèsAbhi 3667fd82b7 feat: docs 2023-10-01 18:56:25 -05:00
TrèsAbhi 691d5cde27 feat: updated embed docs 2023-10-01 18:52:11 -05:00
TrèsAbhi 7f431b07e1 feat: added options 2023-10-01 18:43:40 -05:00
TrèsAbhi 8264f19ad4 fix: center aligned text in breakdown 2023-10-01 17:45:53 -05:00
TrèsAbhi e947cfba45 feat: ico 2023-10-01 17:43:48 -05:00
TrèsAbhi e69590c035 feat: added favicon 2023-10-01 17:38:28 -05:00
TrèsAbhi 8b19393d7c feat: updated README to be better lol 2023-10-01 17:36:03 -05:00
TrèsAbhi 9480603572 feat: added title and meta desc 2023-10-01 17:26:08 -05:00
TrèsAbhi 90c6535a4c fix: center aligned blitzkrieg logo 2023-10-01 17:24:02 -05:00
TrèsAbhi 258588921a Update cd-host.yml 2023-10-01 17:16:16 -05:00
TrèsAbhi 525b309037 Update ci-build-bot.yml 2023-10-01 17:15:47 -05:00
TrèsAbhi 2f941b9224 Update ci-build-docs.yml 2023-10-01 17:15:36 -05:00
TrèsAbhi 259c4240b5 Update ci-build-website.yml 2023-10-01 17:15:25 -05:00
TrèsAbhi 9eb7157fce Update cd-host.yml 2023-10-01 17:15:02 -05:00
21 changed files with 417 additions and 195 deletions

No files matched your search

+1 -1
View File
@@ -29,4 +29,4 @@ jobs:
unzip bot.zip unzip bot.zip
- name: Run bot - name: Run bot
run: NODE_ENV=production GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} WARGAMING_APPLICATION_ID=${{ WARGAMING_APPLICATION_ID }} node --enable-source-maps --trace-warnings bot/index.cjs run: NODE_ENV=production GH_TOKEN=${{ secrets.GH_TOKEN }} DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }} node --enable-source-maps --trace-warnings index.cjs
+1 -1
View File
@@ -1,4 +1,4 @@
name: CI Build name: CI Build Bot
on: [push, pull_request] on: [push, pull_request]
+1 -1
View File
@@ -1,4 +1,4 @@
name: CI Build name: CI Build Docs
on: [push, pull_request] on: [push, pull_request]
+1 -1
View File
@@ -1,4 +1,4 @@
name: CI Build name: CI Build Website
on: [push, pull_request] on: [push, pull_request]
+31 -8
View File
@@ -1,21 +1,44 @@
# blitzkrieg # Blitzkrieg
A Discord bot for anything and everything related to World of Tanks Blitz. [![built with Codeium](https://codeium.com/badges/main)](https://codeium.com)
## Invite the Bot Tools for everything World of Tanks Blitz
Join the [Discord server](https://discord.gg/nDt7AjGJQH), click on the bot's profile, and click "Add to Server". ## Invite the bot
## Using Go to [application directory for Blitzkrieg](https://discord.com/application-directory/1097673957865443370) and click "Add to Server." Voila! You're ready to go.
### Installing dependencies ![Blitzkrieg application directory](https://i.imgur.com/JlEA0J1.png)
## Visit the website
### https://blitz-krieg.vercel.app/
![Blitzkrieg website](https://i.imgur.com/QOXUP63.png)
## Visit the documentation website
### https://tresabhi.github.io/blitzkrieg/
![Blitzkrieg documentation website](https://i.imgur.com/mCZCZEI.png)
## Run the bot
### Install dependencies
```bash ```bash
yarn yarn
``` ```
### Running ### Create `.env`
```env
GH_TOKEN={YOUR_GITHUB_TOKEN_HERE}
DISCORD_TOKEN={YOUR_DISCORD_TOKE_HERE}
```
### Run the command
```bash ```bash
yarn start discord-token=<token> wargaming-application-id=<id> yarn dev:bot
``` ```
+17
View File
@@ -0,0 +1,17 @@
# Blitzkrieg 1.13.1
The quickest minor update in town 🤠
## Changes
- Added a title, description, and icon to the website
- Added display options to the session tracker
## Fixes
- Center aligned stats in breakdown to work better on low width devices
- Center aligned the Blitzkrieg logo in the navbar
## Technical Changes
- Added up-to-date information to `README.md` and embed docs
+7
View File
@@ -0,0 +1,7 @@
# Blitzkrieg 1.13.2
One lonely albeit important fix.
## Fixes
- Gradients for `/stats` now use Radix's new hex values
+12 -12
View File
@@ -1,23 +1,23 @@
# Embeds # Embeds
Embeds are generated by a few commands as a URL that can be loaded in a browser/streaming software. Embeds are URLs generated by commands or tools that represent the "naked" version of the respective command or tool
## Creating Embeds ## Creating Embeds
1. Use a command of your liking (for example, `/stats`). 1. Use a command or tool and click on "Embed" if it's available
2. Customize the command however you like and the URL generated for the embed will match your needs. 2. Make sure to tweak the settings of the command or tool because embeds replicate everything
3. Send the command and when the bot is "done thinking," an "Embed" button will appear below the bot's reply.
## Using Embeds in Streaming Softwares ## Using Embeds in Streaming Softwares
1. Click on the "Embed" button. 1. Click on the "Embed" button
2. Copy the URL from your browser. - If this was generated by a command, copy the URL from the browser
3. Paste the URL into a Browser Source. - If this was generated by a tool, the URL has been copied to your clipboard already
4. Set the refresh rate/FPS to something very low like `0.0167` (that's 1 frame every 60 seconds). 2. Paste this URL into your streaming service as a "Browser Source"
- Tool embeds use local storage so data won't be transferred from you browser to your streaming software
- Make sure to visit the tool's page in your streaming software (using the "Interact" feature) and then open the embed
- Set your browser source refresh rate to `1Hz` because that's how fast embeds refresh
## How Embeds Behave ## How Embeds Behave
- They load like any normal website albeit with a significant delay. - They load like any normal website
- They auto refresh every `60s`. - They auto refresh every `1s`
- All embeds and images generated by Blitzkrieg have a width of `640px` but the height varies and must be found manually through trial and error.
- It's okay to have a Browser Source that has a height larger than needed as Blitzkrieg embeds are transparent.
+3 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "blitzkrieg", "name": "blitzkrieg",
"version": "1.13.0", "version": "1.13.2",
"description": "🎉 All-in-one Discord bot 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",
"scripts": { "scripts": {
@@ -37,6 +37,7 @@
"esbuild": "^0.19.3", "esbuild": "^0.19.3",
"express": "^4.18.2", "express": "^4.18.2",
"fuzzysort": "^2.0.4", "fuzzysort": "^2.0.4",
"immer": "^10.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"markdown-escape": "^2.0.0", "markdown-escape": "^2.0.0",
"next": "^13.5.2", "next": "^13.5.2",
Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

+158 -122
View File
@@ -12,6 +12,7 @@ import { TankopediaEntry, tankopedia } from '../../../core/blitz/tankopedia';
import { tankAverages } from '../../../core/blitzstars/tankAverages'; 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';
const REFRESH_RATE = 1 / 5; const REFRESH_RATE = 1 / 5;
@@ -139,136 +140,171 @@ export default function SessionPage() {
return () => clearInterval(interval); return () => clearInterval(interval);
}, [session]); }, [session]);
const totalRowStats: Record<
CustomColumnDisplay,
Breakdown.RowStatItem | undefined
> = {
battles: {
title: 'Battles',
current: sessionBattles?.toLocaleString(),
career: tankStatsArray
.reduce((accumulator, stats) => accumulator + stats.all.battles, 0)
.toLocaleString(),
},
winrate: {
title: 'Winrate',
current: sessionBattles ? `${(100 * sessionWinrate).toFixed(2)}%` : '--',
career: `${(100 * careerWinrate).toFixed(2)}%`,
delta: sessionBattles === 0 ? 0 : sessionWinrate - careerWinrate,
},
wn8: (() => {
if (!diff) return undefined;
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(),
};
})(),
damage: {
title: 'Damage',
delta: sessionBattles === 0 ? 0 : currentDamage - careerDamage,
current: sessionBattles
? Math.round(currentDamage).toLocaleString()
: '--',
career: Math.round(careerDamage).toLocaleString(),
},
none: undefined,
};
return ( return (
<Breakdown.Root> <Breakdown.Root>
{session.isTracking && since !== undefined && diff !== undefined && ( {session.showTotal &&
<Breakdown.Row session.isTracking &&
title={`Since ${since.toLocaleString(undefined, { since !== undefined &&
hour: 'numeric', diff !== undefined && (
minute: 'numeric', <Breakdown.Row
hour12: true, minimized={!session.showCareer}
month: 'short', title={`Since ${since.toLocaleString(undefined, {
year: 'numeric', hour: 'numeric',
day: 'numeric', minute: 'numeric',
})}`} hour12: true,
stats={[ month: 'short',
{ year: 'numeric',
title: 'Battles', day: 'numeric',
current: sessionBattles!.toLocaleString(), })}`}
career: tankStatsArray stats={session.customColumns.map((customColumn) => {
.reduce( const rowStat = totalRowStats[customColumn.display];
(accumulator, stats) => accumulator + stats.all.battles,
0, if (!rowStat) return undefined;
)
.toLocaleString(),
},
{
title: 'Winrate',
current: sessionBattles
? `${(100 * sessionWinrate).toFixed(2)}%`
: '--',
career: `${(100 * careerWinrate).toFixed(2)}%`,
delta: sessionBattles === 0 ? 0 : sessionWinrate - careerWinrate,
},
(() => {
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 { return {
title: 'WN8', ...rowStat,
current: battlesWithWN8 delta:
? Math.round(currentWN8).toLocaleString() customColumn.showDelta && rowStat.percentile === undefined
: '--', ? rowStat.delta
percentile: battlesWithWN8 : undefined,
? getWN8Percentile(currentWN8)
: undefined,
career: Math.round(diff.careerWN8).toLocaleString(),
}; };
})(), })}
{ />
title: 'Damage', )}
delta: sessionBattles === 0 ? 0 : currentDamage - careerDamage,
current: sessionBattles
? Math.round(currentDamage).toLocaleString()
: '--',
career: Math.round(careerDamage).toLocaleString(),
},
]}
/>
)}
{diff?.list {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 const rowStats: Record<
key={stats.tank_id} CustomColumnDisplay,
title={tankopedia?.name ?? `Unknown tank ${stats.tank_id}`} Breakdown.RowStatItem | undefined
type="tank" > = {
tankType={tankopedia?.type} battles: {
treeType={(() => { title: 'Battles',
if (tankopedia?.is_collectible) return TreeTypeEnum.Collector; current: stats.all.battles.toLocaleString(),
if (tankopedia?.is_premium) return TreeTypeEnum.Premium; career: career.all.battles.toLocaleString(),
})()} },
stats={[ winrate: {
{ title: 'Winrate',
title: 'Battles', current: `${(100 * (stats.all.wins / stats.all.battles)).toFixed(
current: stats.all.battles.toLocaleString(), 2,
career: career.all.battles.toLocaleString(), )}%`,
}, career: `${(100 * (career.all.wins / career.all.battles)).toFixed(
{ 2,
title: 'Winrate', )}%`,
current: `${( delta:
100 * stats.all.wins / stats.all.battles -
(stats.all.wins / stats.all.battles) career.all.wins / career.all.battles,
).toFixed(2)}%`, },
career: `${( wn8: {
100 * title: 'WN8',
(career.all.wins / career.all.battles) current:
).toFixed(2)}%`, currentWN8 === undefined
delta: ? undefined
stats.all.wins / stats.all.battles - : Math.round(currentWN8).toLocaleString(),
career.all.wins / career.all.battles, career:
}, careerWN8 === undefined
{ ? undefined
title: 'WN8', : Math.round(careerWN8).toLocaleString(),
current: percentile:
currentWN8 === undefined currentWN8 === undefined
? undefined ? undefined
: Math.round(currentWN8).toLocaleString(), : getWN8Percentile(currentWN8),
career: },
careerWN8 === undefined damage: {
? undefined title: 'Damage',
: Math.round(careerWN8).toLocaleString(), current: Math.round(
percentile: stats.all.damage_dealt / stats.all.battles,
currentWN8 === undefined ).toLocaleString(),
? undefined career: Math.round(
: getWN8Percentile(currentWN8), career.all.damage_dealt / career.all.battles,
}, ).toLocaleString(),
{ delta:
title: 'Damage', stats.all.damage_dealt / stats.all.battles -
current: Math.round( career.all.damage_dealt / career.all.battles,
stats.all.damage_dealt / stats.all.battles, },
).toLocaleString(), none: undefined,
career: Math.round( };
career.all.damage_dealt / career.all.battles,
).toLocaleString(), return (
delta: <Breakdown.Row
stats.all.damage_dealt / stats.all.battles - key={stats.tank_id}
career.all.damage_dealt / career.all.battles, 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> </Breakdown.Root>
); );
} }
+8
View File
@@ -25,6 +25,14 @@ export default function RootLayout({ children }: RootLayoutProps) {
return ( return (
<html lang="en" className={robotoFlex.className}> <html lang="en" className={robotoFlex.className}>
<head>
<title>Blitzkrieg</title>
<meta
name="description"
content="🎉 Tools for everything World of Tanks Blitz"
/>
</head>
<body <body
style={{ style={{
margin: 0, margin: 0,
@@ -0,0 +1,66 @@
import { DropdownMenu } from '@radix-ui/themes';
import mutateSession, { useSession } from '../../../../stores/session';
export type CustomColumnDisplay =
| 'battles'
| 'winrate'
| 'wn8'
| 'damage'
| 'none';
const NO_DELTA: CustomColumnDisplay[] = ['battles', 'wn8', 'none'];
interface CustomColumn {
column: number;
}
export function CustomColumn({ column }: CustomColumn) {
const customColumn = useSession((state) => state.customColumns[column]);
return (
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger>Column {column + 1}</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Label>Display</DropdownMenu.Label>
<DropdownMenu.RadioGroup
value={customColumn.display}
onValueChange={(event) =>
mutateSession((draft) => {
draft.customColumns[column].display =
event as CustomColumnDisplay;
})
}
>
<DropdownMenu.RadioItem value="battles">
Battles
</DropdownMenu.RadioItem>
<DropdownMenu.RadioItem value="winrate">
Winrate
</DropdownMenu.RadioItem>
<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) && (
<>
<DropdownMenu.Separator />
<DropdownMenu.CheckboxItem
checked={customColumn.showDelta}
onCheckedChange={(checked) =>
mutateSession((draft) => {
draft.customColumns[column].showDelta = checked;
})
}
>
Show delta
</DropdownMenu.CheckboxItem>
</>
)}
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
);
}
+44 -3
View File
@@ -1,6 +1,11 @@
'use client'; 'use client';
import { CopyIcon, PersonIcon, ReloadIcon } from '@radix-ui/react-icons'; import {
CaretDownIcon,
CopyIcon,
PersonIcon,
ReloadIcon,
} from '@radix-ui/react-icons';
import { import {
AlertDialog, AlertDialog,
Button, Button,
@@ -23,10 +28,9 @@ 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 * as styles from './page.css'; import * as styles from './page.css';
// 1 once per 5 seconds
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<
@@ -150,6 +154,7 @@ export default function Page() {
<AlertDialog.Root> <AlertDialog.Root>
<AlertDialog.Trigger> <AlertDialog.Trigger>
<Button <Button
variant="soft"
onClick={(event) => { onClick={(event) => {
if (!session.promptBeforeReset) { if (!session.promptBeforeReset) {
event.preventDefault(); event.preventDefault();
@@ -206,6 +211,7 @@ export default function Page() {
<Tooltip content="Copy to clipboard"> <Tooltip content="Copy to clipboard">
<Button <Button
variant="soft"
className={styles.toolbarButton} className={styles.toolbarButton}
onClick={() => onClick={() =>
navigator.clipboard.writeText( navigator.clipboard.writeText(
@@ -216,6 +222,41 @@ export default function Page() {
<CopyIcon width="16" height="16" /> Embed <CopyIcon width="16" height="16" /> Embed
</Button> </Button>
</Tooltip> </Tooltip>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button variant="soft" className={styles.toolbarButton}>
Options
<CaretDownIcon />
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<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>
</div> </div>
</div> </div>
@@ -1,5 +1,4 @@
import { Percentile } from '../../../../../constants/percentiles'; import { Percentile } from '../../../../../constants/percentiles';
import hslToHex from '../../../../../core/node/hslToHex';
import { PERCENTILE_COLORS } from '../../../../PercentileIndicator'; import { PERCENTILE_COLORS } from '../../../../PercentileIndicator';
export enum GlowSide { export enum GlowSide {
@@ -14,8 +13,8 @@ export interface GlowProps {
export function Glow({ percentile, side }: GlowProps) { export function Glow({ percentile, side }: GlowProps) {
const color = PERCENTILE_COLORS[percentile]; const color = PERCENTILE_COLORS[percentile];
const color1 = hslToHex(color); const color1 = color;
const color2 = `${color1}00`; const color2 = `${color}00`;
const background = `linear-gradient(${ const background = `linear-gradient(${
side === GlowSide.Top ? 0 : 180 side === GlowSide.Top ? 0 : 180
+22 -17
View File
@@ -4,6 +4,14 @@ import { theme } from '../../../stitches.config';
import { TREE_TYPE_ICONS, TreeTypeEnum } from '../../Tanks'; import { TREE_TYPE_ICONS, TreeTypeEnum } from '../../Tanks';
import { RowStat } from './RowStat'; import { RowStat } from './RowStat';
export interface RowStatItem {
title: string;
current?: number | string;
career?: number | string;
delta?: number;
percentile?: Percentile;
}
interface RowProps { interface RowProps {
type?: 'tank' | 'summary'; type?: 'tank' | 'summary';
title: string; title: string;
@@ -11,13 +19,7 @@ interface RowProps {
treeType?: TreeTypeEnum; treeType?: TreeTypeEnum;
tankType?: TankType; tankType?: TankType;
stats: { stats: (RowStatItem | undefined)[];
title: string;
current?: number | string;
career?: number | string;
delta?: number;
percentile?: Percentile;
}[];
} }
export function Row(props: RowProps) { export function Row(props: RowProps) {
@@ -77,16 +79,19 @@ export function Row(props: RowProps) {
paddingBottom: 8, paddingBottom: 8,
}} }}
> >
{props.stats.map((row, index) => ( {props.stats.map(
<RowStat (row, index) =>
minimized={props.minimized} row && (
key={index} <RowStat
name={`${row.title} • ${row.career ?? '--'}`} minimized={props.minimized}
value={row.current ?? '--'} key={index}
delta={row.delta} name={`${row.title} • ${row.career ?? '--'}`}
percentile={row.percentile} value={row.current ?? '--'}
/> delta={row.delta}
))} percentile={row.percentile}
/>
),
)}
</div> </div>
</div> </div>
); );
@@ -55,6 +55,7 @@ export function RowStat({
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
fontWeight: 'bold', fontWeight: 'bold',
fontSize: 16, fontSize: 16,
textAlign: 'center',
}} }}
> >
{value} {value}
@@ -62,7 +63,13 @@ export function RowStat({
</div> </div>
{!minimized && ( {!minimized && (
<span style={{ color: theme.colors.textLowContrast, fontSize: 12 }}> <span
style={{
color: theme.colors.textLowContrast,
textAlign: 'center',
fontSize: 12,
}}
>
{name} {name}
</span> </span>
)} )}
+9 -1
View File
@@ -19,7 +19,15 @@ export default function Navbar() {
{/* <Button> {/* <Button>
<HamburgerMenuIcon /> <HamburgerMenuIcon />
</Button> */} </Button> */}
<Link href="/" style={{ color: 'inherit' }}> <Link
href="/"
style={{
color: 'inherit',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<BlitzkriegWormWide /> <BlitzkriegWormWide />
</Link> </Link>
{/* <Button> {/* <Button>
-22
View File
@@ -1,22 +0,0 @@
const numberRegex = /\d+(\.\d+)?/g;
/**
* Thanks to icl7126!
* https://stackoverflow.com/a/44134328/12294756
*/
export default function hslToHex(hsl: string) {
let [h, s, l] = Array.from(hsl.matchAll(numberRegex)).map((m) =>
parseFloat(m[0]),
);
l /= 100;
const a = (s * Math.min(l, 1 - l)) / 100;
const f = (n: number) => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color)
.toString(16)
.padStart(2, '0'); // convert to Hex and prefix "0" if needed
};
return `#${f(0)}${f(8)}${f(4)}`;
}
+21
View File
@@ -1,5 +1,7 @@
import { produce } from 'immer';
import { create } from 'zustand'; import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware'; import { devtools, persist } from 'zustand/middleware';
import { CustomColumnDisplay } from '../app/tools/session/components/CustomColumn';
import { Region } from '../constants/regions'; import { Region } from '../constants/regions';
import { NormalizedTankStats } from '../types/tanksStats'; import { NormalizedTankStats } from '../types/tanksStats';
@@ -18,6 +20,12 @@ type Session = (
} }
) & { ) & {
promptBeforeReset: boolean; promptBeforeReset: boolean;
customColumns: {
display: CustomColumnDisplay;
showDelta: boolean;
}[] & { length: 4 };
showTotal: boolean;
showCareer: boolean;
}; };
export const useSession = create<Session>()( export const useSession = create<Session>()(
@@ -26,6 +34,15 @@ export const useSession = create<Session>()(
(set) => ({ (set) => ({
isTracking: false, isTracking: false,
promptBeforeReset: true, promptBeforeReset: true,
showTotal: true,
showCareer: true,
customColumns: [
{ display: 'battles', showDelta: false },
{ display: 'winrate', showDelta: true },
{ display: 'wn8', showDelta: false },
{ display: 'damage', showDelta: true },
],
}), }),
{ {
name: 'session', name: 'session',
@@ -33,3 +50,7 @@ export const useSession = create<Session>()(
), ),
), ),
); );
export default function mutateSession(recipe: (draft: Session) => void) {
useSession.setState(produce(recipe));
}
+5
View File
@@ -3078,6 +3078,11 @@ ignore@^5.1.8, ignore@^5.2.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
immer@^10.0.2:
version "10.0.2"
resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.2.tgz#11636c5b77acf529e059582d76faf338beb56141"
integrity sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==
indent-string@^4.0.0: indent-string@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"