mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: playlist
This commit is contained in:
1 parent
5ba64f231a
commit
ed0dde37d2
10 files changed
+141
-11
No files matched your search
@@ -859,6 +859,14 @@
|
||||
},
|
||||
|
||||
"tools": {
|
||||
"playlist": {
|
||||
"name": "Playlist",
|
||||
"description": "Tank playlist generator",
|
||||
"seo_description": "Don't know what to play in World of Tanks Blitz? Use BlitzKit Playlist to get suggestions!",
|
||||
"button": "Generate",
|
||||
"pool_size": "{count} possible tanks"
|
||||
},
|
||||
|
||||
"mixer": {
|
||||
"name": "Mixer",
|
||||
"button": "Mix and match",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 208 KiB |
@@ -4,11 +4,11 @@
|
||||
|
||||
const isAPI = window.location.pathname.startsWith("/api");
|
||||
const isI18nPath = locales.supported.some(({ locale }) =>
|
||||
window.location.pathname.startsWith(`/${locale}`)
|
||||
window.location.pathname.startsWith(`/${locale}/`)
|
||||
);
|
||||
const bestLocale = getBestLocale();
|
||||
const currentPath = locales.supported.find(({ locale }) =>
|
||||
window.location.pathname.startsWith(`/${locale}`)
|
||||
window.location.pathname.startsWith(`/${locale}/`)
|
||||
);
|
||||
|
||||
if (
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { tankIcon } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n";
|
||||
import { Box, Flex, Grid, Text } from "@radix-ui/themes";
|
||||
import { useMemo } from "react";
|
||||
import usePromise from "react-promise-suspense";
|
||||
import { awaitableTankDefinitions } from "../core/awaitables/tankDefinitions";
|
||||
import { filterTanks } from "../core/blitzkit/filterTanks";
|
||||
import { useLocale } from "../hooks/useLocale";
|
||||
import { TankFilters } from "../stores/tankFilters";
|
||||
import { FilterControl } from "./TankSearch/components/FilterControl";
|
||||
|
||||
const tankDefinitions = await awaitableTankDefinitions;
|
||||
|
||||
export function PlaylistPool() {
|
||||
const { strings } = useLocale();
|
||||
const filters = TankFilters.use();
|
||||
const randomTankList = useMemo(() => {
|
||||
const list = Object.values(tankDefinitions.tanks);
|
||||
|
||||
for (let i = list.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[list[i], list[j]] = [list[j], list[i]];
|
||||
}
|
||||
|
||||
return list;
|
||||
}, [filters]);
|
||||
const tanks = usePromise(
|
||||
() => filterTanks(filters, randomTankList),
|
||||
// react-promise-suspense has awful type annotations
|
||||
[filters] as any
|
||||
);
|
||||
const tanksTrimmed = useMemo(() => tanks.slice(0, 48), [filters]);
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="4" align="center">
|
||||
<FilterControl />
|
||||
|
||||
<Text color="gray">
|
||||
{literals(strings.website.tools.playlist.pool_size, {
|
||||
count: tanks.length,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
<Box position="relative" height="100%" width="100%" overflow="hidden">
|
||||
<Grid
|
||||
columns="repeat(auto-fill, minmax(4rem, 1fr))"
|
||||
flow="dense"
|
||||
overflow="hidden"
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
width="100%"
|
||||
>
|
||||
{tanksTrimmed.map((tank) => (
|
||||
<Box
|
||||
key={tank.id}
|
||||
style={{
|
||||
aspectRatio: "4 / 3",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
backgroundImage: `url(${tankIcon(tank.id)})`,
|
||||
backgroundRepeat: "no-repeat",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
<Box
|
||||
position="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(transparent, var(--gray-1) calc(100% - 2rem))",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -35,14 +35,18 @@ export const tools: Record<string, Tool> = {
|
||||
disabled: true,
|
||||
button: "bronze",
|
||||
},
|
||||
guess: {
|
||||
id: "guess",
|
||||
button: "cyan",
|
||||
playlist: {
|
||||
id: "playlist",
|
||||
button: "tomato",
|
||||
},
|
||||
mixer: {
|
||||
id: "mixer",
|
||||
button: "gray",
|
||||
},
|
||||
guess: {
|
||||
id: "guess",
|
||||
button: "cyan",
|
||||
},
|
||||
gallery: {
|
||||
id: "gallery",
|
||||
button: "gold",
|
||||
|
||||
@@ -16,11 +16,11 @@ export interface Props {
|
||||
noFooter?: boolean;
|
||||
noNavbar?: boolean;
|
||||
integrated?: boolean;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
const servicing = import.meta.env.SERVICING;
|
||||
const { noFooter, noNavbar, integrated } = Astro.props;
|
||||
const locale = Astro.params.locale;
|
||||
const { noFooter, noNavbar, integrated, locale } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang={locale}>
|
||||
|
||||
@@ -7,10 +7,10 @@ import BaseLayout from "./BaseLayout.astro";
|
||||
|
||||
interface Props {
|
||||
tool: Tool;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
const { tool } = Astro.props;
|
||||
const { locale } = Astro.params;
|
||||
const { tool, locale } = Astro.props;
|
||||
const strings = getStrings(locale);
|
||||
const toolStrings =
|
||||
strings.website.tools[
|
||||
@@ -20,7 +20,7 @@ const toolStrings =
|
||||
|
||||
<ToolOpenGraph {tool} />
|
||||
|
||||
<BaseLayout>
|
||||
<BaseLayout {locale}>
|
||||
<Box
|
||||
style={{
|
||||
backgroundImage: `url(/assets/images/tools/${tool.id}.webp)`,
|
||||
|
||||
@@ -12,7 +12,7 @@ const { locale } = Astro.params;
|
||||
|
||||
<ToolOpenGraph tool={tools.performance} />
|
||||
|
||||
<BaseLayout noFooter>
|
||||
<BaseLayout {locale} noFooter>
|
||||
<Page client:only="react" {locale}>
|
||||
<Page skeleton slot="fallback" {locale} />
|
||||
</Page>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { PageWrapper } from "../../../components/PageWrapper";
|
||||
import { PlaylistPool } from "../../../components/PlaylistFilters";
|
||||
import { LocaleProvider } from "../../../hooks/useLocale";
|
||||
import type { MaybeSkeletonComponentProps } from "../../../types/maybeSkeletonComponentProps";
|
||||
|
||||
interface Props {
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export function Page({
|
||||
locale,
|
||||
skeleton,
|
||||
}: Props & MaybeSkeletonComponentProps) {
|
||||
return (
|
||||
<LocaleProvider locale={locale}>
|
||||
<PageWrapper color="tomato" direction="row">
|
||||
<PlaylistPool />
|
||||
</PageWrapper>
|
||||
</LocaleProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
import { tools } from "../../../constants/tools";
|
||||
import ToolLayout from "../../../layouts/ToolLayout.astro";
|
||||
import { Page } from "./_index.tsx";
|
||||
|
||||
export { getStaticPaths } from "../index.astro";
|
||||
|
||||
const { locale } = Astro.params;
|
||||
---
|
||||
|
||||
<ToolLayout {locale} tool={tools.playlist}>
|
||||
<Page {locale} client:only="react">
|
||||
<Page {locale} skeleton slot="fallback" />
|
||||
</Page>
|
||||
</ToolLayout>
|
||||
Reference in new issue
Block a user