mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-25 19:49:50 +01:00
feat: filter owned tanks
This commit is contained in:
1 parent
66cf590f1c
commit
9412be2d7c
10 files changed
+435
-354
No files matched your search
@@ -750,6 +750,7 @@
|
|||||||
"select_dialog_title": "Search for tanks",
|
"select_dialog_title": "Search for tanks",
|
||||||
"swap_dialog_title": "Swap tank",
|
"swap_dialog_title": "Swap tank",
|
||||||
"clear_filters": "Clear filters",
|
"clear_filters": "Clear filters",
|
||||||
|
"login": "Login to use this",
|
||||||
"search_bar_hint": "Search tanks...",
|
"search_bar_hint": "Search tanks...",
|
||||||
"recent": "Recently viewed",
|
"recent": "Recently viewed",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { getStrings } from "../core/i18n/getStrings";
|
|||||||
import { Var } from "../core/radix/var";
|
import { Var } from "../core/radix/var";
|
||||||
import { LinkI18n } from "./LinkI18n";
|
import { LinkI18n } from "./LinkI18n";
|
||||||
import { tools } from "../constants/tools";
|
import { tools } from "../constants/tools";
|
||||||
|
import { NavbarAccount } from "./NavbarAccount";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
integrated?: boolean;
|
integrated?: boolean;
|
||||||
@@ -182,7 +183,7 @@ const strings = getStrings(locale);
|
|||||||
<GearIcon width="1em" height="1em" />
|
<GearIcon width="1em" height="1em" />
|
||||||
</LinkI18n>
|
</LinkI18n>
|
||||||
|
|
||||||
<!-- <NavbarAccount client:idle /> -->
|
<NavbarAccount {locale} client:idle />
|
||||||
|
|
||||||
<Flex align="center" display={{ initial: "flex", sm: "none" }}>
|
<Flex align="center" display={{ initial: "flex", sm: "none" }}>
|
||||||
<IconButton variant="ghost" color="gray" className="hamburger">
|
<IconButton variant="ghost" color="gray" className="hamburger">
|
||||||
@@ -200,7 +201,7 @@ const strings = getStrings(locale);
|
|||||||
display={{ initial: "flex", sm: "none" }}
|
display={{ initial: "flex", sm: "none" }}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
toolsFiltered.map(( tool) => {
|
toolsFiltered.map((tool) => {
|
||||||
const toolStrings =
|
const toolStrings =
|
||||||
strings.website.tools[
|
strings.website.tools[
|
||||||
(tool.strings ?? tool.id) as keyof typeof strings.website.tools
|
(tool.strings ?? tool.id) as keyof typeof strings.website.tools
|
||||||
|
|||||||
@@ -8,13 +8,25 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from "@radix-ui/themes";
|
} from "@radix-ui/themes";
|
||||||
import { Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
|
import {
|
||||||
|
LocaleProvider,
|
||||||
|
type LocaleAcceptorProps,
|
||||||
|
} from "../../hooks/useLocale";
|
||||||
import { App } from "../../stores/app";
|
import { App } from "../../stores/app";
|
||||||
import { BlitzKitTheme } from "../BlitzKitTheme";
|
import { BlitzKitTheme } from "../BlitzKitTheme";
|
||||||
import { WargamingAccountName } from "../WargamingAccountName";
|
import { WargamingAccountName } from "../WargamingAccountName";
|
||||||
import { WargamingIcon } from "../WargamingIcon";
|
import { WargamingIcon } from "../WargamingIcon";
|
||||||
import { WargamingLoginButton } from "../WargamingLoginButton";
|
import { WargamingLoginButton } from "../WargamingLoginButton";
|
||||||
|
|
||||||
export function NavbarAccount() {
|
export function NavbarAccount({ locale }: LocaleAcceptorProps) {
|
||||||
|
return (
|
||||||
|
<LocaleProvider locale={locale}>
|
||||||
|
<Content />
|
||||||
|
</LocaleProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Content() {
|
||||||
const logins = App.use((state) => state.logins);
|
const logins = App.use((state) => state.logins);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ import {
|
|||||||
TankType,
|
TankType,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from "@blitzkit/core";
|
} from "@blitzkit/core";
|
||||||
|
import {
|
||||||
|
ComponentBooleanIcon,
|
||||||
|
LockClosedIcon,
|
||||||
|
LockOpen2Icon,
|
||||||
|
} from "@radix-ui/react-icons";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
@@ -13,15 +18,17 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
Popover,
|
Popover,
|
||||||
Text,
|
Text,
|
||||||
|
Tooltip,
|
||||||
|
type FlexProps,
|
||||||
} from "@radix-ui/themes";
|
} from "@radix-ui/themes";
|
||||||
import { isEqual, times } from "lodash-es";
|
import { isEqual, times } from "lodash-es";
|
||||||
import { awaitableGameDefinitions } from "../../../core/awaitables/gameDefinitions";
|
import { awaitableGameDefinitions } from "../../../core/awaitables/gameDefinitions";
|
||||||
import { Var } from "../../../core/radix/var";
|
import { Var } from "../../../core/radix/var";
|
||||||
import { useLocale } from "../../../hooks/useLocale";
|
import { useLocale } from "../../../hooks/useLocale";
|
||||||
|
import { App } from "../../../stores/app";
|
||||||
import { TankFilters } from "../../../stores/tankFilters";
|
import { TankFilters } from "../../../stores/tankFilters";
|
||||||
import { TankSort } from "../../../stores/tankopediaSort";
|
import { TankSort } from "../../../stores/tankopediaSort";
|
||||||
import { classIcons } from "../../ClassIcon";
|
import { classIcons } from "../../ClassIcon";
|
||||||
import { ExperimentIcon } from "../../ExperimentIcon";
|
|
||||||
import { GunAutoloaderIcon } from "../../GunAutoloaderIcon";
|
import { GunAutoloaderIcon } from "../../GunAutoloaderIcon";
|
||||||
import { GunAutoreloaderIcon } from "../../GunAutoreloaderIcon";
|
import { GunAutoreloaderIcon } from "../../GunAutoreloaderIcon";
|
||||||
import { GunRegularIcon } from "../../GunRegularIcon";
|
import { GunRegularIcon } from "../../GunRegularIcon";
|
||||||
@@ -108,28 +115,80 @@ function ShellFilter({ index, premium }: { index: number; premium?: boolean }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FilterControl({ compact }: FilterControlProps) {
|
function OwnershipFilter(props: FlexProps) {
|
||||||
|
const tankFilters = TankFilters.use();
|
||||||
|
const wargaming = App.use((state) => state.logins.wargaming);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
overflow="hidden"
|
||||||
|
style={{ borderRadius: "var(--radius-full)" }}
|
||||||
|
direction="column"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
disabled={!wargaming}
|
||||||
|
variant={tankFilters.ownership === "all" ? "solid" : "soft"}
|
||||||
|
radius="none"
|
||||||
|
color={tankFilters.ownership === "all" ? undefined : "gray"}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
draft.ownership = "all";
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ComponentBooleanIcon />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
disabled={!wargaming}
|
||||||
|
variant={tankFilters.ownership === "owned" ? "solid" : "soft"}
|
||||||
|
radius="none"
|
||||||
|
color={tankFilters.ownership === "owned" ? undefined : "gray"}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
draft.ownership = "owned";
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LockOpen2Icon />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
disabled={!wargaming}
|
||||||
|
variant={tankFilters.ownership === "unowned" ? "solid" : "soft"}
|
||||||
|
radius="none"
|
||||||
|
color={tankFilters.ownership === "unowned" ? undefined : "gray"}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
draft.ownership = "unowned";
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LockClosedIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FilterControl() {
|
||||||
const tankFilters = TankFilters.use();
|
const tankFilters = TankFilters.use();
|
||||||
const { strings } = useLocale();
|
const { strings } = useLocale();
|
||||||
const clearable = !isEqual(tankFilters, TankFilters.initial);
|
const clearable = !isEqual(tankFilters, TankFilters.initial);
|
||||||
|
const wargaming = App.use((state) => state.logins.wargaming);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" align="center">
|
<Flex direction="column" align="center">
|
||||||
<Flex
|
<Flex height="fit-content" gap="1" align="start" justify="center">
|
||||||
height="fit-content"
|
|
||||||
gap="2"
|
|
||||||
align={{ initial: "start", md: "center" }}
|
|
||||||
justify="center"
|
|
||||||
>
|
|
||||||
<Flex
|
<Flex
|
||||||
flexShrink="0"
|
flexShrink="0"
|
||||||
direction={compact ? "row" : { initial: "row", md: "column" }}
|
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
style={{ borderRadius: "var(--radius-5)" }}
|
style={{ borderRadius: "var(--radius-4)" }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex direction="column">
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
{times(5, (index) => {
|
{times(5, (index) => {
|
||||||
const tier = index + 1;
|
const tier = index + 1;
|
||||||
const selected = tankFilters.tiers?.includes(tier);
|
const selected = tankFilters.tiers?.includes(tier);
|
||||||
@@ -167,9 +226,7 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Flex
|
<Flex direction="column">
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
{times(5, (index) => {
|
{times(5, (index) => {
|
||||||
const tier = index + 6;
|
const tier = index + 6;
|
||||||
const selected = tankFilters.tiers?.includes(tier);
|
const selected = tankFilters.tiers?.includes(tier);
|
||||||
@@ -202,13 +259,10 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
flexShrink="0"
|
flexShrink="0"
|
||||||
direction={compact ? "row" : { initial: "row", md: "column" }}
|
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
style={{ borderRadius: "var(--radius-5)" }}
|
style={{ borderRadius: "var(--radius-4)" }}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex direction="column">
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
{gameDefinitions.nations
|
{gameDefinitions.nations
|
||||||
.slice(0, Math.ceil(gameDefinitions.nations.length / 2))
|
.slice(0, Math.ceil(gameDefinitions.nations.length / 2))
|
||||||
.map((nation) => {
|
.map((nation) => {
|
||||||
@@ -250,9 +304,7 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Flex
|
<Flex direction="column">
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
{gameDefinitions.nations
|
{gameDefinitions.nations
|
||||||
.slice(Math.ceil(gameDefinitions.nations.length / 2))
|
.slice(Math.ceil(gameDefinitions.nations.length / 2))
|
||||||
.map((nation) => {
|
.map((nation) => {
|
||||||
@@ -289,298 +341,274 @@ export function FilterControl({ compact }: FilterControlProps) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex
|
<Flex
|
||||||
direction={compact ? "row" : { initial: "row", md: "column" }}
|
overflow="hidden"
|
||||||
align="start"
|
style={{ borderRadius: "var(--radius-full)" }}
|
||||||
gap={{ initial: "2", md: "1" }}
|
direction="column"
|
||||||
>
|
>
|
||||||
<Flex gap="2" align="start">
|
{TANK_CLASSES.map((tankClass) => {
|
||||||
<Flex
|
const Icon = classIcons[tankClass];
|
||||||
overflow="hidden"
|
const selected = tankFilters.classes?.includes(tankClass);
|
||||||
style={{ borderRadius: "var(--radius-full)" }}
|
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
{TANK_CLASSES.map((tankClass) => {
|
|
||||||
const Icon = classIcons[tankClass];
|
|
||||||
const selected = tankFilters.classes?.includes(tankClass);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
|
||||||
key={tankClass}
|
|
||||||
variant={selected ? "solid" : "soft"}
|
|
||||||
radius="none"
|
|
||||||
color={selected ? undefined : "gray"}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
if (tankFilters.classes.includes(tankClass)) {
|
|
||||||
draft.classes = draft.classes.filter(
|
|
||||||
(c) => c !== tankClass
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.classes = [...draft.classes, tankClass];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon style={{ width: "1em", height: "1em" }} />
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
overflow="hidden"
|
|
||||||
style={{ borderRadius: "var(--radius-full)" }}
|
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
variant={
|
key={tankClass}
|
||||||
tankFilters.gunType.includes("regular") ? "solid" : "soft"
|
variant={selected ? "solid" : "soft"}
|
||||||
}
|
|
||||||
radius="none"
|
radius="none"
|
||||||
color={
|
color={selected ? undefined : "gray"}
|
||||||
tankFilters.gunType.includes("regular") ? undefined : "gray"
|
|
||||||
}
|
|
||||||
highContrast
|
highContrast
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
TankFilters.mutate((draft) => {
|
TankFilters.mutate((draft) => {
|
||||||
if (tankFilters.gunType.includes("regular")) {
|
if (tankFilters.classes.includes(tankClass)) {
|
||||||
draft.gunType = draft.gunType.filter(
|
draft.classes = draft.classes.filter(
|
||||||
(t) => t !== "regular"
|
(c) => c !== tankClass
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
draft.gunType = [...draft.gunType, "regular"];
|
draft.classes = [...draft.classes, tankClass];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GunRegularIcon style={{ width: "1em", height: "1em" }} />
|
<Icon style={{ width: "1em", height: "1em" }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
);
|
||||||
variant={
|
})}
|
||||||
tankFilters.gunType.includes("auto_loader") ? "solid" : "soft"
|
|
||||||
}
|
|
||||||
radius="none"
|
|
||||||
color={
|
|
||||||
tankFilters.gunType.includes("auto_loader")
|
|
||||||
? undefined
|
|
||||||
: "gray"
|
|
||||||
}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
if (tankFilters.gunType.includes("auto_loader")) {
|
|
||||||
draft.gunType = draft.gunType.filter(
|
|
||||||
(t) => t !== "auto_loader"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.gunType = [...draft.gunType, "auto_loader"];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GunAutoloaderIcon style={{ width: "1em", height: "1em" }} />
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
variant={
|
|
||||||
tankFilters.gunType.includes("auto_reloader")
|
|
||||||
? "solid"
|
|
||||||
: "soft"
|
|
||||||
}
|
|
||||||
radius="none"
|
|
||||||
color={
|
|
||||||
tankFilters.gunType.includes("auto_reloader")
|
|
||||||
? undefined
|
|
||||||
: "gray"
|
|
||||||
}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
if (tankFilters.gunType.includes("auto_reloader")) {
|
|
||||||
draft.gunType = draft.gunType.filter(
|
|
||||||
(t) => t !== "auto_reloader"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.gunType = [...draft.gunType, "auto_reloader"];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GunAutoreloaderIcon style={{ width: "1em", height: "1em" }} />
|
|
||||||
</IconButton>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
overflow="hidden"
|
|
||||||
style={{ borderRadius: "var(--radius-full)" }}
|
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
<ShellFilter index={0} />
|
|
||||||
<ShellFilter index={1} premium />
|
|
||||||
<ShellFilter index={2} />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="2" align="start">
|
|
||||||
<Flex
|
|
||||||
overflow="hidden"
|
|
||||||
style={{ borderRadius: "var(--radius-full)" }}
|
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
variant={
|
|
||||||
tankFilters.types?.includes(TankType.RESEARCHABLE)
|
|
||||||
? "solid"
|
|
||||||
: "soft"
|
|
||||||
}
|
|
||||||
radius="none"
|
|
||||||
color={
|
|
||||||
tankFilters.types?.includes(TankType.RESEARCHABLE)
|
|
||||||
? undefined
|
|
||||||
: "gray"
|
|
||||||
}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
if (tankFilters.types.includes(TankType.RESEARCHABLE)) {
|
|
||||||
draft.types = draft.types.filter(
|
|
||||||
(t) => t !== TankType.RESEARCHABLE
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.types = [...draft.types, TankType.RESEARCHABLE];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ResearchedIcon style={{ width: "1em", height: "1em" }} />
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
variant={
|
|
||||||
tankFilters.types?.includes(TankType.PREMIUM)
|
|
||||||
? "solid"
|
|
||||||
: "soft"
|
|
||||||
}
|
|
||||||
radius="none"
|
|
||||||
color={
|
|
||||||
tankFilters.types?.includes(TankType.PREMIUM)
|
|
||||||
? "amber"
|
|
||||||
: "gray"
|
|
||||||
}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
if (tankFilters.types.includes(TankType.PREMIUM)) {
|
|
||||||
draft.types = draft.types.filter(
|
|
||||||
(t) => t !== TankType.PREMIUM
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.types = [...draft.types, TankType.PREMIUM];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={
|
|
||||||
tankFilters.types?.includes(TankType.PREMIUM)
|
|
||||||
? undefined
|
|
||||||
: "amber"
|
|
||||||
}
|
|
||||||
style={{ display: "flex", justifyContent: "center" }}
|
|
||||||
>
|
|
||||||
<ResearchedIcon style={{ width: "1em", height: "1em" }} />
|
|
||||||
</Text>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
variant={
|
|
||||||
tankFilters.types?.includes(TankType.COLLECTOR)
|
|
||||||
? "solid"
|
|
||||||
: "soft"
|
|
||||||
}
|
|
||||||
radius="none"
|
|
||||||
color={
|
|
||||||
tankFilters.types?.includes(TankType.COLLECTOR)
|
|
||||||
? "blue"
|
|
||||||
: "gray"
|
|
||||||
}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
if (tankFilters.types.includes(TankType.COLLECTOR)) {
|
|
||||||
draft.types = draft.types.filter(
|
|
||||||
(t) => t !== TankType.COLLECTOR
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
draft.types = [...draft.types, TankType.COLLECTOR];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
color={
|
|
||||||
tankFilters.types?.includes(TankType.COLLECTOR)
|
|
||||||
? undefined
|
|
||||||
: "blue"
|
|
||||||
}
|
|
||||||
style={{ display: "flex", justifyContent: "center" }}
|
|
||||||
>
|
|
||||||
<ResearchedIcon style={{ width: "1em", height: "1em" }} />
|
|
||||||
</Text>
|
|
||||||
</IconButton>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
overflow="hidden"
|
|
||||||
style={{ borderRadius: "var(--radius-full)" }}
|
|
||||||
direction={compact ? "column" : { md: "row", initial: "column" }}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
variant={tankFilters.testing === "include" ? "solid" : "soft"}
|
|
||||||
radius="none"
|
|
||||||
color={tankFilters.testing === "include" ? undefined : "gray"}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.testing = "include";
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ExperimentIcon
|
|
||||||
style={{ width: "1em", height: "1em", color: "currentColor" }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
variant={tankFilters.testing === "exclude" ? "solid" : "soft"}
|
|
||||||
radius="none"
|
|
||||||
color={tankFilters.testing === "exclude" ? undefined : "gray"}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.testing = "exclude";
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ScienceOffIcon
|
|
||||||
style={{ width: "1em", height: "1em", color: "currentColor" }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
|
||||||
variant={tankFilters.testing === "only" ? "solid" : "soft"}
|
|
||||||
radius="none"
|
|
||||||
color={tankFilters.testing === "only" ? undefined : "gray"}
|
|
||||||
highContrast
|
|
||||||
onClick={() => {
|
|
||||||
TankFilters.mutate((draft) => {
|
|
||||||
draft.testing = "only";
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ScienceIcon
|
|
||||||
style={{ width: "1em", height: "1em", color: "currentColor" }}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
overflow="hidden"
|
||||||
|
style={{ borderRadius: "var(--radius-full)" }}
|
||||||
|
direction="column"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
variant={tankFilters.gunType.includes("regular") ? "solid" : "soft"}
|
||||||
|
radius="none"
|
||||||
|
color={tankFilters.gunType.includes("regular") ? undefined : "gray"}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
if (tankFilters.gunType.includes("regular")) {
|
||||||
|
draft.gunType = draft.gunType.filter((t) => t !== "regular");
|
||||||
|
} else {
|
||||||
|
draft.gunType = [...draft.gunType, "regular"];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GunRegularIcon style={{ width: "1em", height: "1em" }} />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
variant={
|
||||||
|
tankFilters.gunType.includes("auto_loader") ? "solid" : "soft"
|
||||||
|
}
|
||||||
|
radius="none"
|
||||||
|
color={
|
||||||
|
tankFilters.gunType.includes("auto_loader") ? undefined : "gray"
|
||||||
|
}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
if (tankFilters.gunType.includes("auto_loader")) {
|
||||||
|
draft.gunType = draft.gunType.filter(
|
||||||
|
(t) => t !== "auto_loader"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.gunType = [...draft.gunType, "auto_loader"];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GunAutoloaderIcon style={{ width: "1em", height: "1em" }} />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
variant={
|
||||||
|
tankFilters.gunType.includes("auto_reloader") ? "solid" : "soft"
|
||||||
|
}
|
||||||
|
radius="none"
|
||||||
|
color={
|
||||||
|
tankFilters.gunType.includes("auto_reloader") ? undefined : "gray"
|
||||||
|
}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
if (tankFilters.gunType.includes("auto_reloader")) {
|
||||||
|
draft.gunType = draft.gunType.filter(
|
||||||
|
(t) => t !== "auto_reloader"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.gunType = [...draft.gunType, "auto_reloader"];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GunAutoreloaderIcon style={{ width: "1em", height: "1em" }} />
|
||||||
|
</IconButton>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
overflow="hidden"
|
||||||
|
style={{ borderRadius: "var(--radius-full)" }}
|
||||||
|
direction="column"
|
||||||
|
>
|
||||||
|
<ShellFilter index={0} />
|
||||||
|
<ShellFilter index={1} premium />
|
||||||
|
<ShellFilter index={2} />
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
overflow="hidden"
|
||||||
|
style={{ borderRadius: "var(--radius-full)" }}
|
||||||
|
direction="column"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
variant={
|
||||||
|
tankFilters.types?.includes(TankType.RESEARCHABLE)
|
||||||
|
? "solid"
|
||||||
|
: "soft"
|
||||||
|
}
|
||||||
|
radius="none"
|
||||||
|
color={
|
||||||
|
tankFilters.types?.includes(TankType.RESEARCHABLE)
|
||||||
|
? undefined
|
||||||
|
: "gray"
|
||||||
|
}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
if (tankFilters.types.includes(TankType.RESEARCHABLE)) {
|
||||||
|
draft.types = draft.types.filter(
|
||||||
|
(t) => t !== TankType.RESEARCHABLE
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.types = [...draft.types, TankType.RESEARCHABLE];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ResearchedIcon style={{ width: "1em", height: "1em" }} />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
variant={
|
||||||
|
tankFilters.types?.includes(TankType.PREMIUM) ? "solid" : "soft"
|
||||||
|
}
|
||||||
|
radius="none"
|
||||||
|
color={
|
||||||
|
tankFilters.types?.includes(TankType.PREMIUM) ? "amber" : "gray"
|
||||||
|
}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
if (tankFilters.types.includes(TankType.PREMIUM)) {
|
||||||
|
draft.types = draft.types.filter(
|
||||||
|
(t) => t !== TankType.PREMIUM
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.types = [...draft.types, TankType.PREMIUM];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
color={
|
||||||
|
tankFilters.types?.includes(TankType.PREMIUM)
|
||||||
|
? undefined
|
||||||
|
: "amber"
|
||||||
|
}
|
||||||
|
style={{ display: "flex", justifyContent: "center" }}
|
||||||
|
>
|
||||||
|
<ResearchedIcon style={{ width: "1em", height: "1em" }} />
|
||||||
|
</Text>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
variant={
|
||||||
|
tankFilters.types?.includes(TankType.COLLECTOR) ? "solid" : "soft"
|
||||||
|
}
|
||||||
|
radius="none"
|
||||||
|
color={
|
||||||
|
tankFilters.types?.includes(TankType.COLLECTOR) ? "blue" : "gray"
|
||||||
|
}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
if (tankFilters.types.includes(TankType.COLLECTOR)) {
|
||||||
|
draft.types = draft.types.filter(
|
||||||
|
(t) => t !== TankType.COLLECTOR
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
draft.types = [...draft.types, TankType.COLLECTOR];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
color={
|
||||||
|
tankFilters.types?.includes(TankType.COLLECTOR)
|
||||||
|
? undefined
|
||||||
|
: "blue"
|
||||||
|
}
|
||||||
|
style={{ display: "flex", justifyContent: "center" }}
|
||||||
|
>
|
||||||
|
<ResearchedIcon style={{ width: "1em", height: "1em" }} />
|
||||||
|
</Text>
|
||||||
|
</IconButton>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
overflow="hidden"
|
||||||
|
style={{ borderRadius: "var(--radius-full)" }}
|
||||||
|
direction="column"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
variant={tankFilters.testing === "include" ? "solid" : "soft"}
|
||||||
|
radius="none"
|
||||||
|
color={tankFilters.testing === "include" ? undefined : "gray"}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
draft.testing = "include";
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ComponentBooleanIcon />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
variant={tankFilters.testing === "exclude" ? "solid" : "soft"}
|
||||||
|
radius="none"
|
||||||
|
color={tankFilters.testing === "exclude" ? undefined : "gray"}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
draft.testing = "exclude";
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ScienceOffIcon
|
||||||
|
style={{ width: "1em", height: "1em", color: "currentColor" }}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
variant={tankFilters.testing === "only" ? "solid" : "soft"}
|
||||||
|
radius="none"
|
||||||
|
color={tankFilters.testing === "only" ? undefined : "gray"}
|
||||||
|
highContrast
|
||||||
|
onClick={() => {
|
||||||
|
TankFilters.mutate((draft) => {
|
||||||
|
draft.testing = "only";
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ScienceIcon
|
||||||
|
style={{ width: "1em", height: "1em", color: "currentColor" }}
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{wargaming ? (
|
||||||
|
<OwnershipFilter />
|
||||||
|
) : (
|
||||||
|
<Tooltip content={strings.website.common.tank_search.login}>
|
||||||
|
<OwnershipFilter />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{clearable && (
|
{clearable && (
|
||||||
|
|||||||
@@ -11,13 +11,15 @@ import { Callout, Flex, Link, Text, type FlexProps } from "@radix-ui/themes";
|
|||||||
import fuzzysort from "fuzzysort";
|
import fuzzysort from "fuzzysort";
|
||||||
import { times, uniq } from "lodash-es";
|
import { times, uniq } from "lodash-es";
|
||||||
import { memo, useEffect, useMemo, useState } from "react";
|
import { memo, useEffect, useMemo, useState } from "react";
|
||||||
|
import usePromise from "react-promise-suspense";
|
||||||
import { awaitableGameDefinitions } from "../../core/awaitables/gameDefinitions";
|
import { awaitableGameDefinitions } from "../../core/awaitables/gameDefinitions";
|
||||||
import { awaitableModelDefinitions } from "../../core/awaitables/modelDefinitions";
|
import { awaitableModelDefinitions } from "../../core/awaitables/modelDefinitions";
|
||||||
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
||||||
import { awaitableTankNames } from "../../core/awaitables/tankNames";
|
import { awaitableTankNames } from "../../core/awaitables/tankNames";
|
||||||
import { filterTank } from "../../core/blitzkit/filterTank";
|
import { filterTanks } from "../../core/blitzkit/filterTanks";
|
||||||
import { resolveReload } from "../../core/blitzkit/resolveReload";
|
import { resolveReload } from "../../core/blitzkit/resolveReload";
|
||||||
import { useLocale } from "../../hooks/useLocale";
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
|
import { App } from "../../stores/app";
|
||||||
import { TankFilters } from "../../stores/tankFilters";
|
import { TankFilters } from "../../stores/tankFilters";
|
||||||
import { TankopediaPersistent } from "../../stores/tankopediaPersistent";
|
import { TankopediaPersistent } from "../../stores/tankopediaPersistent";
|
||||||
import { SORT_UNITS } from "../../stores/tankopediaPersistent/constants";
|
import { SORT_UNITS } from "../../stores/tankopediaPersistent/constants";
|
||||||
@@ -54,15 +56,34 @@ const [gameDefinitions, modelDefinitions, tankDefinitions, tankNames] =
|
|||||||
export const TankSearch = memo<TankSearchProps>(
|
export const TankSearch = memo<TankSearchProps>(
|
||||||
({ compact, onSelect, onSelectAll, skeleton, ...props }) => {
|
({ compact, onSelect, onSelectAll, skeleton, ...props }) => {
|
||||||
const { strings, locale } = useLocale();
|
const { strings, locale } = useLocale();
|
||||||
|
const wargaming = App.use((state) => state.logins.wargaming);
|
||||||
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
||||||
const tankopediaSort = TankSort.use();
|
const tankopediaSort = TankSort.use();
|
||||||
const tankFilters = TankFilters.use();
|
const tankFilters = TankFilters.use();
|
||||||
const tanksFiltered = useMemo(() => {
|
|
||||||
|
const filtered = usePromise(
|
||||||
|
() => {
|
||||||
|
if (tankFilters.search === null) {
|
||||||
|
return filterTanks(
|
||||||
|
tankFilters,
|
||||||
|
awaitedTanksDefinitionsArray,
|
||||||
|
wargaming?.id
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(
|
||||||
|
fuzzysort
|
||||||
|
.go(tankFilters.search, tankNames, {
|
||||||
|
keys: SEARCH_KEYS,
|
||||||
|
})
|
||||||
|
.map((result) => tankDefinitions.tanks[result.obj.id])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// react-promise-suspense has awful type annotations
|
||||||
|
[tankFilters] as any
|
||||||
|
);
|
||||||
|
const sorted = useMemo(() => {
|
||||||
if (tankFilters.search === null) {
|
if (tankFilters.search === null) {
|
||||||
const filtered = awaitedTanksDefinitionsArray.filter((tank) =>
|
|
||||||
filterTank(tankFilters, tank)
|
|
||||||
);
|
|
||||||
let sorted: TankDefinition[];
|
let sorted: TankDefinition[];
|
||||||
|
|
||||||
switch (tankopediaSort.by) {
|
switch (tankopediaSort.by) {
|
||||||
@@ -396,17 +417,12 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
? sorted
|
? sorted
|
||||||
: sorted.reverse();
|
: sorted.reverse();
|
||||||
} else {
|
} else {
|
||||||
const searchedRaw = fuzzysort.go(tankFilters.search, tankNames, {
|
return filtered;
|
||||||
keys: SEARCH_KEYS,
|
|
||||||
});
|
|
||||||
const searchedTanks = searchedRaw.map(
|
|
||||||
(result) => tankDefinitions.tanks[result.obj.id]
|
|
||||||
);
|
|
||||||
return searchedTanks;
|
|
||||||
}
|
}
|
||||||
}, [tankFilters, tankopediaSort]);
|
}, [tankFilters, tankopediaSort]);
|
||||||
|
|
||||||
const [loadedCards, setLoadedCards] = useState(DEFAULT_LOADED_CARDS);
|
const [loadedCards, setLoadedCards] = useState(DEFAULT_LOADED_CARDS);
|
||||||
const tanks = tanksFiltered.slice(0, loadedCards);
|
const tanks = sorted.slice(0, loadedCards);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoadedCards(DEFAULT_LOADED_CARDS);
|
setLoadedCards(DEFAULT_LOADED_CARDS);
|
||||||
@@ -420,19 +436,17 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!tankFilters.search && !tankFilters.searching && (
|
{!tankFilters.search && !tankFilters.searching && <FilterControl />}
|
||||||
<FilterControl compact={compact} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!skeleton && !compact && <RecentlyViewed />}
|
{!skeleton && !compact && <RecentlyViewed />}
|
||||||
|
|
||||||
<Flex mt="2" gap="1" align="center" justify="center" direction="column">
|
<Flex mt="2" gap="1" align="center" justify="center" direction="column">
|
||||||
<Flex gap="2">
|
<Flex gap="2">
|
||||||
<Text color="gray">
|
<Text color="gray">
|
||||||
{tanksFiltered.length === 1
|
{sorted.length === 1
|
||||||
? strings.website.common.tank_search.count_singular
|
? strings.website.common.tank_search.count_singular
|
||||||
: literals(strings.website.common.tank_search.count_plural, {
|
: literals(strings.website.common.tank_search.count_plural, {
|
||||||
count: tanksFiltered.length.toLocaleString(locale),
|
count: sorted.length.toLocaleString(locale),
|
||||||
})}
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
@@ -442,10 +456,10 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
href="#"
|
href="#"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
onSelectAll(tanksFiltered);
|
onSelectAll(sorted);
|
||||||
TankopediaPersistent.mutate((draft) => {
|
TankopediaPersistent.mutate((draft) => {
|
||||||
draft.recentlyViewed = uniq([
|
draft.recentlyViewed = uniq([
|
||||||
...tanksFiltered.map(({ id }) => id),
|
...sorted.map(({ id }) => id),
|
||||||
...draft.recentlyViewed,
|
...draft.recentlyViewed,
|
||||||
])
|
])
|
||||||
.filter((id) => id in tankDefinitions.tanks)
|
.filter((id) => id in tankDefinitions.tanks)
|
||||||
@@ -499,14 +513,14 @@ export const TankSearch = memo<TankSearchProps>(
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{times(
|
{times(
|
||||||
Math.min(PREVIEW_COUNT, tanksFiltered.length - loadedCards),
|
Math.min(PREVIEW_COUNT, sorted.length - loadedCards),
|
||||||
(index) => {
|
(index) => {
|
||||||
return (
|
return (
|
||||||
<SkeletonTankCard
|
<SkeletonTankCard
|
||||||
key={index}
|
key={index}
|
||||||
onIntersection={() => {
|
onIntersection={() => {
|
||||||
setLoadedCards((state) =>
|
setLoadedCards((state) =>
|
||||||
Math.min(state + 2, tanksFiltered.length)
|
Math.min(state + 2, sorted.length)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,30 +1,37 @@
|
|||||||
import type { TankDefinition } from '@blitzkit/core';
|
import type { TankDefinition } from "@blitzkit/core";
|
||||||
import { times } from 'lodash-es';
|
import { times } from "lodash-es";
|
||||||
import type { TankFilters } from '../../stores/tankFilters';
|
import type { TankFilters } from "../../stores/tankFilters";
|
||||||
|
|
||||||
const SHELLS = times(3, (index) => index);
|
const SHELLS = times(3, (index) => index);
|
||||||
|
|
||||||
export function filterTank(filters: TankFilters, tank: TankDefinition) {
|
export async function filterTank(
|
||||||
|
filters: TankFilters,
|
||||||
|
tank: TankDefinition,
|
||||||
|
owned: number[] = []
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
|
(filters.ownership === "all" ||
|
||||||
|
(filters.ownership === "owned" && owned.includes(tank.id)) ||
|
||||||
|
(filters.ownership === "unowned" && !owned.includes(tank.id))) &&
|
||||||
(filters.tiers.length === 0 || filters.tiers.includes(tank.tier)) &&
|
(filters.tiers.length === 0 || filters.tiers.includes(tank.tier)) &&
|
||||||
(filters.nations.length === 0 || filters.nations.includes(tank.nation)) &&
|
(filters.nations.length === 0 || filters.nations.includes(tank.nation)) &&
|
||||||
(filters.classes.length === 0 || filters.classes.includes(tank.class)) &&
|
(filters.classes.length === 0 || filters.classes.includes(tank.class)) &&
|
||||||
(filters.types.length === 0 || filters.types.includes(tank.type)) &&
|
(filters.types.length === 0 || filters.types.includes(tank.type)) &&
|
||||||
(filters.testing === 'include' ||
|
(filters.testing === "include" ||
|
||||||
(filters.testing === 'only' && tank.testing) ||
|
(filters.testing === "only" && tank.testing) ||
|
||||||
(filters.testing === 'exclude' && !tank.testing)) &&
|
(filters.testing === "exclude" && !tank.testing)) &&
|
||||||
(filters.gunType.length === 0 ||
|
(filters.gunType.length === 0 ||
|
||||||
(filters.gunType.includes('regular') &&
|
(filters.gunType.includes("regular") &&
|
||||||
tank.turrets.some((turret) =>
|
tank.turrets.some((turret) =>
|
||||||
turret.guns.some((gun) => gun.gun_type!.$case === 'regular'),
|
turret.guns.some((gun) => gun.gun_type!.$case === "regular")
|
||||||
)) ||
|
)) ||
|
||||||
(filters.gunType.includes('auto_loader') &&
|
(filters.gunType.includes("auto_loader") &&
|
||||||
tank.turrets.some((turret) =>
|
tank.turrets.some((turret) =>
|
||||||
turret.guns.some((gun) => gun.gun_type!.$case === 'auto_loader'),
|
turret.guns.some((gun) => gun.gun_type!.$case === "auto_loader")
|
||||||
)) ||
|
)) ||
|
||||||
(filters.gunType.includes('auto_reloader') &&
|
(filters.gunType.includes("auto_reloader") &&
|
||||||
tank.turrets.some((turret) =>
|
tank.turrets.some((turret) =>
|
||||||
turret.guns.some((gun) => gun.gun_type!.$case === 'auto_reloader'),
|
turret.guns.some((gun) => gun.gun_type!.$case === "auto_reloader")
|
||||||
))) &&
|
))) &&
|
||||||
tank.turrets.some((turret) =>
|
tank.turrets.some((turret) =>
|
||||||
turret.guns.some((gun) =>
|
turret.guns.some((gun) =>
|
||||||
@@ -32,9 +39,9 @@ export function filterTank(filters: TankFilters, tank: TankDefinition) {
|
|||||||
(index) =>
|
(index) =>
|
||||||
filters.shells[index] === null ||
|
filters.shells[index] === null ||
|
||||||
gun.shells[index] === undefined ||
|
gun.shells[index] === undefined ||
|
||||||
gun.shells[index].type === filters.shells[index],
|
gun.shells[index].type === filters.shells[index]
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { getTankStats, idToRegion, TankDefinition } from "@blitzkit/core";
|
||||||
|
import { TankFilters } from "../../stores/tankFilters";
|
||||||
|
import { filterTank } from "./filterTank";
|
||||||
|
|
||||||
|
export async function filterTanks(
|
||||||
|
filters: TankFilters,
|
||||||
|
tanks: TankDefinition[],
|
||||||
|
player?: number
|
||||||
|
) {
|
||||||
|
const filtered: TankDefinition[] = [];
|
||||||
|
let owned: number[] | undefined;
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
const tankStats = await getTankStats(idToRegion(player), player, {
|
||||||
|
fields: "tank_id",
|
||||||
|
});
|
||||||
|
|
||||||
|
owned = tankStats?.map(({ tank_id }) => tank_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tank of tanks) {
|
||||||
|
if (await filterTank(filters, tank, owned)) filtered.push(tank);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { useRef } from 'react';
|
|
||||||
import usePromise from 'react-promise-suspense';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A more stable alternative to `use` using `usePromise`.
|
|
||||||
*/
|
|
||||||
export function useAwait<Type>(promise: Promise<Type>) {
|
|
||||||
const callback = useRef(async () => await promise);
|
|
||||||
return usePromise(callback.current, []);
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ export interface WargamingLogin {
|
|||||||
expires: number;
|
expires: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface App {
|
export interface App {
|
||||||
developerMode: boolean;
|
developerMode: boolean;
|
||||||
policiesAgreementIndex: number;
|
policiesAgreementIndex: number;
|
||||||
logins: {
|
logins: {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export interface TankFilters {
|
|||||||
searching: boolean;
|
searching: boolean;
|
||||||
gunType: CaseType<GunDefinition>[];
|
gunType: CaseType<GunDefinition>[];
|
||||||
shells: [ShellType | null, ShellType | null, ShellType | null];
|
shells: [ShellType | null, ShellType | null, ShellType | null];
|
||||||
|
ownership: "all" | "owned" | "unowned";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TankFilters = new Varuna<TankFilters>({
|
export const TankFilters = new Varuna<TankFilters>({
|
||||||
@@ -34,4 +35,5 @@ export const TankFilters = new Varuna<TankFilters>({
|
|||||||
searching: false,
|
searching: false,
|
||||||
gunType: [],
|
gunType: [],
|
||||||
shells: [null, null, null],
|
shells: [null, null, null],
|
||||||
|
ownership: "all",
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user