This commit is contained in:
tresabhi committed 2026-08-06 20:01:50 -05:00
1 parent 13cda7aec0
commit b26c5137f3
53 files changed
+236 -932

No files matched your search

@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { Flex, Heading, IconButton, Table } from "@radix-ui/themes";
import type { TankCharacteristics } from "../../core/blitzkit/tankCharacteristics";
import { useLocale } from "../../hooks/useLocale";
@@ -57,7 +58,10 @@ export function Body({ stats }: BodyProps) {
alt={unwrap(thisShell.name!)}
width={16}
height={16}
src={`/api/icons/shells/${thisShell.icon}.webp`}
src={alias(
"api",
`/icons/shells/${thisShell.icon}.webp`,
)}
/>
</IconButton>
))}
@@ -1,4 +1,4 @@
import { availableProvisions, permanentSkills } from "@blitzkit/core";
import { alias, availableProvisions, permanentSkills } from "@blitzkit/core";
import { checkConsumableProvisionInclusivity } from "@blitzkit/core/src/blitzkit/checkConsumableProvisionInclusivity";
import { ComponentPlaceholderIcon } from "@radix-ui/react-icons";
import {
@@ -231,7 +231,10 @@ export function CompareTable({ stats }: CompareTableProps) {
alt={module}
width={24}
height={24}
src={`/api/icons/modules/${module}.webp`}
src={alias(
"api",
`/icons/modules/${module}.webp`,
)}
style={{
marginLeft: index > 0 ? -8 : 0,
objectFit: "contain",
@@ -320,7 +323,10 @@ export function CompareTable({ stats }: CompareTableProps) {
.name!,
)}
key={provision}
src={`/api//icons/provisions/${provision}.webp`}
src={alias(
"api",
`//icons/provisions/${provision}.webp`,
)}
style={{
left: "50%",
/**
@@ -525,7 +531,10 @@ export function CompareTable({ stats }: CompareTableProps) {
].name!,
)}
key={consumable}
src={`/api/icons/consumables/${consumable}.webp`}
src={alias(
"api",
`/icons/consumables/${consumable}.webp`,
)}
style={{
left: "50%",
/**
@@ -617,7 +626,7 @@ export function CompareTable({ stats }: CompareTableProps) {
variant={camouflage ? "solid" : "outline"}
>
<img
src={`/api/icons/camo.webp`}
src={alias("api", `/icons/camo.webp`)}
style={{
width: "1em",
height: "1em",
@@ -1,4 +1,4 @@
import { permanentSkills, romanize } from "@blitzkit/core";
import { alias, permanentSkills, romanize } from "@blitzkit/core";
import { Flex } from "@radix-ui/themes";
import { api } from "../core/blitzkit/api";
import { GenericTankComponentButton } from "./ModuleButtons/GenericTankComponentButton";
@@ -27,7 +27,7 @@ export function CrewSkillManager({
special={!permanentSkills.includes(skill)}
selected={level > 0}
discriminator={level === 0 ? undefined : romanize(level)}
icon={`/api/icons/skills/${skill}.webp`}
icon={alias("api", `/icons/skills/${skill}.webp`)}
onClick={(event) => {
if (!onChange) return;
@@ -1,4 +1,4 @@
import { Avatar } from "@blitzkit/core";
import { alias, Avatar } from "@blitzkit/core";
import { Cross1Icon, DownloadIcon } from "@radix-ui/react-icons";
import {
Dialog,
@@ -18,7 +18,10 @@ type GalleryCardProps = MaybeSkeletonComponentProps<{
}>;
export function GalleryCard(props: GalleryCardProps) {
const src = `/api/gallery/avatars/${!props.skeleton && `${props.avatar.id}${props.avatar.extension}`}`;
const src = alias(
"api",
`/gallery/avatars/${!props.skeleton && `${props.avatar.id}${props.avatar.extension}`}`,
);
const card = useRef<HTMLDivElement>(null!);
const { unwrap } = useLocale();
+5 -1
View File
@@ -1,4 +1,5 @@
import {
alias,
SEARCH_KEYS,
TankDefinition,
TIER_ROMAN_NUMERALS,
@@ -149,7 +150,10 @@ export function Guesser() {
prefix={
<img
style={{ width: "1em", height: "1em" }}
src={`/api/flags/circle/${result.nation}.webp`}
src={alias(
"api",
`/flags/circle/${result.nation}.webp`,
)}
/>
}
discriminator={
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { GenericTankComponentButton } from "./GenericTankComponentButton";
import type { TankComponentButtonProps } from "./TankComponentButton";
@@ -8,7 +9,7 @@ interface ModuleProps extends TankComponentButtonProps {
export function ModuleButton({ module, ...props }: ModuleProps) {
return (
<GenericTankComponentButton
icon={`/api/icons/modules/${module}.webp`}
icon={alias("api", `/icons/modules/${module}.webp`)}
iconStyles={{
top: "50%",
left: "50%",
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { api } from "../../core/blitzkit/api";
import { useLocale } from "../../hooks/useLocale";
import { GenericTankComponentButton } from "./GenericTankComponentButton";
@@ -15,7 +16,7 @@ export function ProvisionButton({ provision, ...props }: ProvisionButtonProps) {
return (
<GenericTankComponentButton
tooltip={unwrap(provisionDefinitions.provisions[provision].name!)}
icon={`/api/icons/provisions/${provision}.webp`}
icon={alias("api", `/icons/provisions/${provision}.webp`)}
{...props}
/>
);
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { GenericTankComponentButton } from "./GenericTankComponentButton";
import type { TankComponentButtonProps } from "./TankComponentButton";
@@ -8,7 +9,7 @@ interface ShellButtonProps extends TankComponentButtonProps {
export function ShellButton({ shell, ...props }: ShellButtonProps) {
return (
<GenericTankComponentButton
icon={`/api/icons/shells/${shell}.webp`}
icon={alias("api", `/icons/shells/${shell}.webp`)}
{...props}
/>
);
@@ -1,3 +1,5 @@
import { alias } from "@blitzkit/core";
interface SmallTankIconProps {
id: number;
size?: number;
@@ -17,7 +19,7 @@ export function SmallTankIcon({ id, size = HEIGHT }: SmallTankIconProps) {
>
<img
alt={`Tank ${id}`}
src={`/api/tanks/${id}/icons/small.webp`}
src={alias("api", `/tanks/${id}/icons/small.webp`)}
style={{
transform: `scale(${(size / HEIGHT) * 100}%)`,
transformOrigin: "top left",
@@ -1,4 +1,4 @@
import { TankType, type TankDefinition } from "@blitzkit/core";
import { alias, TankType, type TankDefinition } from "@blitzkit/core";
import { Flex, Text, type TextProps } from "@radix-ui/themes";
import { uniq } from "lodash-es";
import { forwardRef, type ReactNode } from "react";
@@ -60,7 +60,10 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
className="tank-search-card"
data-provide-link={provideLink}
style={{
backgroundImage: `url(/api/flags/scratched/${tank.nation}.webp)`,
backgroundImage: `url(${alias(
"api",
`/flags/scratched/${tank.nation}.webp`,
)})`,
...style,
}}
{...props}
@@ -76,7 +79,7 @@ export const TankCard = forwardRef<HTMLSpanElement, TankCardProps>(
>
<img
alt={unwrap(tank.name!)}
src={`/api/tanks/${tank.id}/icons/big.webp`}
src={alias("api", `/tanks/${tank.id}/icons/big.webp`)}
className="image"
draggable={false}
/>
@@ -1,4 +1,5 @@
import {
alias,
GunDefinition,
ShellType,
TANK_CLASSES,
@@ -309,7 +310,7 @@ function NationsFilter() {
width: "1.25em",
height: "1.25em",
}}
src={`/api/flags/circle/${nation}.webp`}
src={alias("api", `/flags/circle/${nation}.webp`)}
/>
))}
</Flex>
@@ -346,7 +347,7 @@ function NationsFilter() {
right="0"
mr="-2"
style={{
backgroundImage: `url(${`/api/flags/scratched/${nation}.webp`})`,
backgroundImage: `url(${alias("api", `/flags/scratched/${nation}.webp`)})`,
backgroundSize: "100%",
backgroundRepeat: "no-repeat",
backgroundPosition: "5rem center",
@@ -679,9 +680,10 @@ function IndividualShellFilter({
{shells[index] !== null && (
<img
style={{ width: "1em", height: "1em" }}
src={`/api/icons/shells/${shellTypeIcons[shells[index]]}${
premium ? "_premium" : ""
}.webp`}
src={alias(
"api",
`/icons/shells/${shellTypeIcons[shells[index]]}${premium ? "_premium" : ""}.webp`,
)}
/>
)}
</IconButton>
@@ -712,9 +714,12 @@ function IndividualShellFilter({
color={selected ? undefined : "gray"}
>
<img
src={`/api/icons/shells/${shellTypeIcons[shellType]}${
premium ? "_premium" : ""
}.webp`}
src={alias(
"api",
`/icons/shells/${shellTypeIcons[shellType]}${
premium ? "_premium" : ""
}.webp`,
)}
style={{ width: "1.25em", height: "1.25em" }}
/>
@@ -883,7 +888,7 @@ function ConsumablesFilter() {
height: "1.25em",
objectFit: "contain",
}}
src={`/api/icons/consumables/${consumable}.webp`}
src={alias("api", `/icons/consumables/${consumable}.webp`)}
/>
))}
@@ -928,7 +933,7 @@ function ConsumablesFilter() {
height: "1.25em",
objectFit: "contain",
}}
src={`/api/icons/consumables/${consumable}.webp`}
src={alias("api", `/icons/consumables/${consumable}.webp`)}
/>
{unwrap(consumableDefinition.name!)}
@@ -977,7 +982,7 @@ function ProvisionsFilter() {
height: "1.25em",
objectFit: "contain",
}}
src={`/api/icons/provisions/${provision}.webp`}
src={alias("api", `/icons/provisions/${provision}.webp`)}
/>
))}
@@ -1022,7 +1027,7 @@ function ProvisionsFilter() {
height: "1.25em",
objectFit: "contain",
}}
src={`/api/icons/provisions/${provision}.webp`}
src={alias("api", `/icons/provisions/${provision}.webp`)}
/>
{unwrap(provisionDefinition.name!)}
@@ -1151,7 +1156,10 @@ function GameModeAbilitiesFilter() {
height: "1.25em",
objectFit: "contain",
}}
src={`/api/icons/consumables/${consumable}.webp`}
src={alias(
"api",
`/icons/consumables/${consumable}.webp`,
)}
/>
{unwrap(abilityDefinition.name!)}
@@ -1200,7 +1208,10 @@ function GameModeAbilitiesFilter() {
height: "1.25em",
objectFit: "contain",
}}
src={`/api/icons/provisions/${provision}.webp`}
src={alias(
"api",
`/icons/provisions/${provision}.webp`,
)}
/>
{unwrap(powerDefinition.name!)}
@@ -1,4 +1,4 @@
import { isExplosive } from "@blitzkit/core";
import { alias, isExplosive } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Flex, Heading, IconButton, Tooltip } from "@radix-ui/themes";
import { useLocale } from "../../../../../../hooks/useLocale";
@@ -62,7 +62,7 @@ export function Firepower({
alt={unwrap(thisShell.name!)}
width={16}
height={16}
src={`/api/icons/shells/${thisShell.icon}.webp`}
src={alias("api", `/icons/shells/${thisShell.icon}.webp`)}
/>
</IconButton>
</Tooltip>
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { Flex, IconButton, Tooltip } from "@radix-ui/themes";
import { useLocale } from "../../../../../../../../hooks/useLocale";
import { Duel } from "../../../../../../../../stores/duel";
@@ -37,7 +38,7 @@ export function ShellSwitcher() {
<img
alt={unwrap(thisShell.name!)}
style={{ width: "1.125rem", height: "1.125rem" }}
src={`/api/icons/shells/${thisShell.icon}.webp`}
src={alias("api", `/icons/shells/${thisShell.icon}.webp`)}
/>
</IconButton>
</Tooltip>
@@ -1,4 +1,4 @@
import { isExplosive } from "@blitzkit/core";
import { alias, isExplosive } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { Box, Flex, Text } from "@radix-ui/themes";
import { useRef, useState } from "react";
@@ -124,7 +124,7 @@ export function RicochetVisualizer({ stats }: StatsAcceptorProps) {
/>
<img
src={`/api/icons/shells/${shell.icon}.webp`}
src={alias("api", `/icons/shells/${shell.icon}.webp`)}
style={{
objectFit: "contain",
transform: "translate(-50%, -50%) rotate(-45deg)",
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { Badge, Box, Flex, Heading, Switch, Text } from "@radix-ui/themes";
import { useLocale } from "../../../../hooks/useLocale";
import { Duel } from "../../../../stores/duel";
@@ -33,7 +34,7 @@ export function Equalizer() {
style={{
cursor: "pointer",
borderRadius: "var(--radius-3)",
backgroundImage: "url(/api/gamemodes/45/banner.webp)",
backgroundImage: `url(${alias("api", `/gamemodes/45/banner.webp`)})`,
backgroundSize: "cover",
backgroundPosition: "center",
overflow: "hidden",
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { Flex, Heading } from "@radix-ui/themes";
import { useLocale } from "../../../../hooks/useLocale";
import { Duel } from "../../../../stores/duel";
@@ -21,7 +22,7 @@ export function Miscellaneous() {
<Flex gap="2" align="center">
{!tank.fixed_camouflage && (
<GenericTankComponentButton
icon={"/api/icons/camo.webp"}
icon={alias("api", "/icons/camo.webp")}
selected={camouflage}
iconStyles={{
top: "50%",
@@ -36,7 +37,7 @@ export function Miscellaneous() {
/>
)}
<GenericTankComponentButton
icon={"/api/icons/boosters/equipment.webp"}
icon={alias("api", "/icons/boosters/equipment.webp")}
selected={cooldownBooster > 0}
banner={
cooldownBooster === 0
@@ -1,4 +1,5 @@
import {
alias,
EngineDefinition,
formatCompact,
GunDefinition,
@@ -110,7 +111,7 @@ function ModuleButton({
<img
alt={unlock.cost!.type}
src={`/api/icons/currencies/${unlock.cost!.type}.webp`}
src={alias("api", `/icons/currencies/${unlock.cost!.type}.webp`)}
style={{
width: 12,
height: 12,
@@ -125,7 +126,10 @@ function ModuleButton({
src={
isTank
? tankIcon(unlock.id)
: `/api/icons/modules/${moduleTypeTypeIconName[unlock.type]}.webp`
: alias(
"api",
`/icons/modules/${moduleTypeTypeIconName[unlock.type]}.webp`,
)
}
style={{
width: isTank ? 64 : 32,
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { Box, Flex, Heading, Text } from "@radix-ui/themes";
import { api } from "../../core/blitzkit/api";
import { useLocale } from "../../hooks/useLocale";
@@ -37,7 +38,10 @@ export function GameModeSection() {
direction="column"
style={{
borderRadius: "var(--radius-3)",
background: `url(/api/gamemodes/${id}/banner.webp)`,
background: `url(${alias(
"api",
`/gamemodes/${id}/banner.webp`,
)})`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
@@ -45,7 +49,10 @@ export function GameModeSection() {
<Box
flexGrow="1"
style={{
background: `url(/api/gamemodes/${id}/banner.webp)`,
background: `url(${alias(
"api",
`/gamemodes/${id}/banner.webp`,
)})`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
@@ -80,7 +87,7 @@ export function GameModeSection() {
height: "1.5em",
objectFit: "contain",
}}
src={`/api/icons/provisions/${id}.webp`}
src={alias("api", `/icons/provisions/${id}.webp`)}
alt={unwrap(provisions.name!)}
/>
{unwrap(provisions.name!)}
@@ -101,7 +108,7 @@ export function GameModeSection() {
height: "1.5em",
objectFit: "contain",
}}
src={`/api/icons/consumables/${id}.webp`}
src={alias("api", `/icons/consumables/${id}.webp`)}
alt={unwrap(consumable.name!)}
/>
{unwrap(consumable.name!)}
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { IconButton } from "@radix-ui/themes";
import { useArmor } from "../../../../../../hooks/useArmor";
import { Duel } from "../../../../../../stores/duel";
@@ -37,7 +38,7 @@ export function DynamicArmorSwitcher() {
>
<img
alt="Dynamic Armor"
src={"/api/icons/consumables/73.webp"}
src={alias("api", "/icons/consumables/73.webp")}
style={{
width: "50%",
height: "50%",
@@ -1,4 +1,5 @@
import {
alias,
resolvePenetrationCoefficient,
TIER_ROMAN_NUMERALS,
uniqueGuns,
@@ -139,7 +140,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
<img
alt="Antagonist Gun"
src={"/api/icons/modules/gun.webp"}
src={alias("api", "/icons/modules/gun.webp")}
style={{
width: "65%",
height: "65%",
@@ -208,7 +209,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
>
<img
alt={unwrap(thisShell.name!)}
src={`/api/icons/shells/${thisShell.icon}.webp`}
src={alias("api", `/icons/shells/${thisShell.icon}.webp`)}
style={{
width: "50%",
height: "50%",
@@ -245,7 +246,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
>
<img
alt="Calibrated Shells"
src={"/api/icons/equipment/103.webp"}
src={alias("api", "/icons/equipment/103.webp")}
style={{
width: "50%",
height: "50%",
@@ -270,7 +271,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
>
<img
alt="Enhanced Armor"
src={"/api/icons/equipment/110.webp"}
src={alias("api", "/icons/equipment/110.webp")}
style={{
width: "50%",
height: "50%",
@@ -1,3 +1,4 @@
import { alias } from "@blitzkit/core";
import { Flex, type FlexProps, Progress } from "@radix-ui/themes";
import { useProgress } from "@react-three/drei";
@@ -11,7 +12,7 @@ export function TankSandboxLoader({ id, ...props }: TankSandboxLoaderProps) {
return (
<Flex width="100%" height="100%" align="center" justify="center" {...props}>
<img
src={`/api/tanks/${id}/icons/big.webp`}
src={alias("api", `/tanks/${id}/icons/big.webp`)}
style={{ height: "75%", filter: "blur(1rem)" }}
/>
<Progress
@@ -1,5 +1,5 @@
import { Flex, Text, type TextProps } from '@radix-ui/themes';
import { Var } from '../../../../core/radix/var';
import { Flex, Text, type TextProps } from "@radix-ui/themes";
import { Var } from "../../../../core/radix/var";
type ListingProps = TextProps & {
label: string;
@@ -11,7 +11,7 @@ export function Listing({ label, ...props }: ListingProps) {
minWidth="17rem"
justify="between"
gap="4"
style={{ borderBottom: `1px solid ${Var('gray-a9')}` }}
style={{ borderBottom: `1px solid ${Var("gray-a9")}` }}
>
<Text size="4">{label}</Text>
<Text align="right" size="4" weight="medium" {...props} />
@@ -1,25 +0,0 @@
import { StarFilledIcon, StarIcon } from '@radix-ui/react-icons';
import { Box, type BoxProps } from '@radix-ui/themes';
type PartialStarProps = BoxProps & {
fill: number;
};
export function PartialStar({ fill, ...props }: PartialStarProps) {
return (
<Box height="1em" width="1em" position="relative" {...props}>
<StarIcon width="1em" height="1em" />
{fill > 0 && (
<Box
top="0"
left="0"
width={`${fill * 100}%`}
position="absolute"
overflow="hidden"
>
<StarFilledIcon width="1em" height="1em" />
</Box>
)}
</Box>
);
}
@@ -1,44 +0,0 @@
import type { StarsInt } from '@blitzkit/core';
import { type TextProps, Flex, Text } from '@radix-ui/themes';
import { times } from 'lodash-es';
import { PartialStar } from './components/PartialStar';
type StarsProps = TextProps & {
stars: number | null;
onCast?: (value: StarsInt) => void;
};
export function Stars({ stars, onCast, ...props }: StarsProps) {
let color: TextProps['color'] = 'green';
if (stars === null) color = 'gray';
else if (stars < 1.5) color = 'tomato';
else if (stars < 2.5) color = 'orange';
else if (stars < 3.5) color = 'amber';
else if (stars < 4.5) color = 'blue';
return (
<Text color={color} {...props}>
<Flex style={{ cursor: onCast ? 'pointer' : 'unset' }}>
{stars !== null &&
times(Math.floor(stars), (index) => (
<PartialStar
fill={1}
key={index}
onClick={() => onCast?.((index + 1) as StarsInt)}
/>
))}
{stars !== null && stars % 1 !== 0 && <PartialStar fill={stars % 1} />}
{times(5 - Math.ceil(stars ?? 0), (index) => (
<PartialStar
fill={0}
key={index}
onClick={() =>
onCast?.((Math.floor(stars ?? 0) + index + 1) as StarsInt)
}
/>
))}
</Flex>
</Text>
);
}
@@ -1,18 +0,0 @@
import { Flex, Text } from '@radix-ui/themes';
import { Stars } from '../../Stars';
interface StarRowProps {
stars: number | null;
children: string;
}
export function StarRow({ stars, children }: StarRowProps) {
return (
<Text color={stars === null ? 'gray' : undefined}>
<Flex align="center" justify="between" width="15rem">
{children}
<Stars stars={stars} />
</Flex>
</Text>
);
}
@@ -1,179 +0,0 @@
import type { BlitzkitResponse, StarsInt } from "@blitzkit/core";
import { PaperPlaneIcon } from "@radix-ui/react-icons";
import {
AlertDialog,
Button,
Dialog,
Flex,
Spinner,
Text,
} from "@radix-ui/themes";
import { useState } from "react";
import { useLocale } from "../../../../../../hooks/useLocale";
import { App } from "../../../../../../stores/app";
import { Duel } from "../../../../../../stores/duel";
import { Stars } from "../../Stars";
export function VoteCaster() {
const tank = Duel.use((state) => state.protagonist.tank);
const [easiness, setEasiness] = useState<StarsInt | null>(null);
const [firepower, setFirepower] = useState<StarsInt | null>(null);
const [maneuverability, setManeuverability] = useState<StarsInt | null>(null);
const [survivability, setSurvivability] = useState<StarsInt | null>(null);
// true = vote successful, false = vote failed, null = hide alert
const [postVote, setPostVote] = useState<boolean | null>(null);
// true = open, false = closed, null = casting
const [showDialogue, setShowDialogue] = useState<boolean | null>(false);
const { unwrap } = useLocale();
return (
<>
<Dialog.Root open={showDialogue !== false} onOpenChange={setShowDialogue}>
<Dialog.Trigger>
<Button variant="surface">
<PaperPlaneIcon /> Vote
</Button>
</Dialog.Trigger>
<Dialog.Content maxWidth="23rem">
<Dialog.Title>Voting for {unwrap(tank.name!)}</Dialog.Title>
<Flex direction="column" mt="4">
<Flex align="center" justify="between">
<Text>
Easiness{" "}
<Text color="gray" size="2">
{easiness === null ? "(empty)" : `(${easiness})`}
</Text>
</Text>
<Stars stars={easiness} size="5" onCast={setEasiness} />
</Flex>
<Flex align="center" justify="between">
<Text>
Firepower{" "}
<Text color="gray" size="2">
{firepower === null ? "(empty)" : `(${firepower})`}
</Text>
</Text>
<Stars stars={firepower} size="5" onCast={setFirepower} />
</Flex>
<Flex align="center" justify="between">
<Text>
Maneuverability{" "}
<Text color="gray" size="2">
{maneuverability === null
? "(empty)"
: `(${maneuverability})`}
</Text>
</Text>
<Stars
stars={maneuverability}
size="5"
onCast={setManeuverability}
/>
</Flex>
<Flex align="center" justify="between">
<Text>
Survivability{" "}
<Text color="gray" size="2">
{survivability === null ? "(empty)" : `(${survivability})`}
</Text>
</Text>
<Stars stars={survivability} size="5" onCast={setSurvivability} />
</Flex>
</Flex>
<Flex justify="end" mt="4" gap="2">
<Dialog.Close>
<Button variant="outline" color="red">
Cancel
</Button>
</Dialog.Close>
<Button
disabled={
showDialogue === null ||
(easiness === null &&
firepower === null &&
maneuverability === null &&
survivability === null)
}
onClick={() => {
const { wargaming } = App.state.logins;
if (!wargaming) {
return setShowDialogue(false);
}
setShowDialogue(null);
fetch(
`/api/tank-voting/${tank.id}/cast?${
easiness === null ? "" : `easiness=${easiness}&`
}${firepower === null ? "" : `firepower=${firepower}&`}${
maneuverability === null
? ""
: `maneuverability=${maneuverability}&`
}${
survivability === null
? ""
: `survivability=${survivability}&`
}player=${wargaming.id}&token=${wargaming.token}`,
)
.then(
(response) => response.json() as Promise<BlitzkitResponse>,
)
.then((json) => {
setShowDialogue(false);
setPostVote(json.status === "ok");
if (json.status === "error") {
console.error("vote failed", json);
}
});
}}
>
{showDialogue === null ? <Spinner /> : <PaperPlaneIcon />} Vote
</Button>
</Flex>
</Dialog.Content>
</Dialog.Root>
<AlertDialog.Root
open={postVote !== null}
onOpenChange={() => setPostVote(null)}
>
{postVote === true && (
<AlertDialog.Content maxWidth="20rem">
<AlertDialog.Title>Vote casted</AlertDialog.Title>
<AlertDialog.Description>
It may take a few minutes for it to show up.
</AlertDialog.Description>
<Flex justify="end" mt="4">
<AlertDialog.Cancel>
<Button>Okay</Button>
</AlertDialog.Cancel>
</Flex>
</AlertDialog.Content>
)}
{postVote === false && (
<AlertDialog.Content maxWidth="20rem">
<AlertDialog.Title>Voting failed</AlertDialog.Title>
<AlertDialog.Description>
So sorry about this! If you're a nerd, feel free to check out the
console.
</AlertDialog.Description>
<Flex justify="end" mt="4">
<AlertDialog.Cancel>
<Button>Gotcha</Button>
</AlertDialog.Cancel>
</Flex>
</AlertDialog.Content>
)}
</AlertDialog.Root>
</>
);
}
@@ -1,91 +0,0 @@
import { formatCompact } from '@blitzkit/core';
import { Flex, Heading, Skeleton, Text } from '@radix-ui/themes';
import { memo } from 'react';
import { useLocale } from '../../../../../hooks/useLocale';
import { useTankVotes } from '../../../../../hooks/useTankVotes';
import { App } from '../../../../../stores/app';
import { Duel } from '../../../../../stores/duel';
import { WargamingLoginButton } from '../../../../WargamingLoginButton';
import { StarRow } from './components/StarRow';
import { VoteCaster } from './components/VoteCaster';
export const Votes = memo(
() => {
const id = Duel.use((state) => state.protagonist.tank.id);
const votes = useTankVotes(id);
const wargaming = App.use((state) => state.logins.wargaming);
const { locale } = useLocale();
return (
<Flex direction="column" gap="3">
<Flex justify="between" align="center">
<Heading size="4">
{!votes && <Skeleton height="1em" width="4rem" />}
{votes && formatCompact(locale, votes.votes)}{' '}
{votes && (votes.votes > 1 ? 'votes' : 'vote')}
</Heading>
{!wargaming && (
<WargamingLoginButton variant="surface">
Login to vote
</WargamingLoginButton>
)}
{wargaming && <VoteCaster />}
</Flex>
<Flex gap="0" direction="column">
<Flex
gap={{ initial: '0', sm: '6' }}
direction={{ initial: 'column', sm: 'row' }}
>
<StarRow
stars={votes?.categories.easiness ?? null}
children="Easiness"
/>
<StarRow
stars={votes?.categories.firepower ?? null}
children="Firepower"
/>
</Flex>
<Flex
gap={{ initial: '0', sm: '6' }}
direction={{ initial: 'column', sm: 'row' }}
>
<StarRow
stars={votes?.categories.maneuverability ?? null}
children="Maneuverability"
/>
<StarRow
stars={votes?.categories.survivability ?? null}
children="Survivability"
/>
</Flex>
<Flex justify="center">
<Text size="2" color="gray" mt="2">
<>
{votes ? (
wargaming ? (
votes.last_updated ? (
`You voted on ${new Date(
votes.last_updated,
).toLocaleDateString()}`
) : (
`You haven't voted yet`
)
) : (
'Login to vote'
)
) : (
<Skeleton height="1em" width="5rem" />
)}
</>
</Text>
</Flex>
</Flex>
</Flex>
);
},
() => true,
);
@@ -1,4 +1,9 @@
import { TankPriceType, TankType, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import {
alias,
TankPriceType,
TankType,
TIER_ROMAN_NUMERALS,
} from "@blitzkit/core";
import { Box, Flex } from "@radix-ui/themes";
import { Var } from "../../../core/radix/var";
import { useLocale } from "../../../hooks/useLocale";
@@ -35,7 +40,7 @@ export function MetaSection() {
>
<Box
style={{
background: `url(${`/api/flags/scratched/${protagonist.nation}.webp`})`,
background: `url(${alias("api", `/flags/scratched/${protagonist.nation}.webp`)})`,
backgroundRepeat: "no-repeat",
backgroundSize: "min(53rem, 80vw)",
backgroundPosition: "-8rem 50%",
@@ -60,7 +65,10 @@ export function MetaSection() {
<img
style={{ width: "1em", height: "1em" }}
alt={protagonist.nation}
src={`/api/flags/circle/${protagonist.nation}.webp`}
src={alias(
"api",
`/flags/circle/${protagonist.nation}.webp`,
)}
/>
{
strings.common.nations[
@@ -130,7 +138,7 @@ export function MetaSection() {
<img
style={{ width: "1em", height: "1em" }}
alt="gold"
src={"/api/icons/currencies/gold.webp"}
src={alias("api", "/icons/currencies/gold.webp")}
/>
</Flex>
</Listing>
@@ -147,12 +155,15 @@ export function MetaSection() {
<img
style={{ width: "1em", height: "1em" }}
alt={TankPriceType[protagonist.price!.type]}
src={`/api/icons/currencies/${
protagonist.price!.type ===
TankPriceType.TANK_PRICE_TYPE_GOLD
? "gold"
: "silver"
}.webp`}
src={alias(
"api",
`/icons/currencies/${
protagonist.price!.type ===
TankPriceType.TANK_PRICE_TYPE_GOLD
? "gold"
: "silver"
}.webp`,
)}
/>
</Flex>
</Listing>
@@ -162,12 +173,15 @@ export function MetaSection() {
<img
style={{ width: "1em", height: "1em" }}
alt={TankPriceType[protagonist.price!.type]}
src={`/api/icons/currencies/${
protagonist.price!.type ===
TankPriceType.TANK_PRICE_TYPE_GOLD
? "gold"
: "silver"
}.webp`}
src={alias(
"api",
`/icons/currencies/${
protagonist.price!.type ===
TankPriceType.TANK_PRICE_TYPE_GOLD
? "gold"
: "silver"
}.webp`,
)}
/>
</Flex>
</Listing>
@@ -183,7 +197,7 @@ export function MetaSection() {
<img
style={{ width: "1em", height: "1em" }}
alt="xp"
src={"/api/icons/currencies/xp.webp"}
src={alias("api", "/icons/currencies/xp.webp")}
/>
</Flex>
</Listing>
@@ -1,4 +1,4 @@
import { formatCompact, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import { alias, formatCompact, TIER_ROMAN_NUMERALS } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n/src/literals";
import { Box, Flex, Skeleton, Text } from "@radix-ui/themes";
import { awaitableAverageDefinitions } from "../../../../core/awaitables/averageDefinitions";
@@ -58,7 +58,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
mx={highlight ? "2" : "0"}
style={{
background: highlight
? `url(${`/api/flags/fade/${tank.nation}.webp`})`
? `url(${alias("api", `/flags/fade/${tank.nation}.webp`)})`
: undefined,
backgroundPosition: "center",
backgroundSize: "cover",
@@ -79,7 +79,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
>
<img
alt={unwrap(tank.name!)}
src={`/api/tanks/${id}/icons/big.webp`}
src={alias("api", `/tanks/${id}/icons/big.webp`)}
width={64}
height={64}
style={{
@@ -99,7 +99,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
<Flex gap="1" align="center">
<img
alt="XP"
src={"/api/icons/currencies/xp.webp"}
src={alias("api", "/icons/currencies/xp.webp")}
style={{
width: "1em",
height: "1em",
@@ -114,7 +114,7 @@ export function Node({ id, highlight, nextIds, skeleton }: NodeProps) {
<Flex gap="1" align="center">
<img
alt="Silver"
src={"/api/icons/currencies/silver.webp"}
src={alias("api", "/icons/currencies/silver.webp")}
style={{
width: "1em",
height: "1em",
@@ -1,4 +1,4 @@
import { TankType } from "@blitzkit/core";
import { alias, TankType } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n/src/literals";
import { CaretLeftIcon, CaretRightIcon, PlusIcon } from "@radix-ui/react-icons";
import { Flex, Heading, IconButton, ScrollArea, Text } from "@radix-ui/themes";
@@ -105,7 +105,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
<Flex gap="1" align="center">
<img
alt="XP"
src={"/api/icons/currencies/xp.webp"}
src={alias("api", "/icons/currencies/xp.webp")}
style={{
width: "1em",
height: "1em",
@@ -121,7 +121,7 @@ export function TechTreeSection({ skeleton }: MaybeSkeletonComponentProps) {
<Flex gap="1" align="center">
<img
alt="Silver"
src={"/api/icons/currencies/silver.webp"}
src={alias("api", "/icons/currencies/silver.webp")}
style={{
width: "1em",
height: "1em",