mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +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": {
|
"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": {
|
"mixer": {
|
||||||
"name": "Mixer",
|
"name": "Mixer",
|
||||||
"button": "Mix and match",
|
"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 isAPI = window.location.pathname.startsWith("/api");
|
||||||
const isI18nPath = locales.supported.some(({ locale }) =>
|
const isI18nPath = locales.supported.some(({ locale }) =>
|
||||||
window.location.pathname.startsWith(`/${locale}`)
|
window.location.pathname.startsWith(`/${locale}/`)
|
||||||
);
|
);
|
||||||
const bestLocale = getBestLocale();
|
const bestLocale = getBestLocale();
|
||||||
const currentPath = locales.supported.find(({ locale }) =>
|
const currentPath = locales.supported.find(({ locale }) =>
|
||||||
window.location.pathname.startsWith(`/${locale}`)
|
window.location.pathname.startsWith(`/${locale}/`)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
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,
|
disabled: true,
|
||||||
button: "bronze",
|
button: "bronze",
|
||||||
},
|
},
|
||||||
guess: {
|
playlist: {
|
||||||
id: "guess",
|
id: "playlist",
|
||||||
button: "cyan",
|
button: "tomato",
|
||||||
},
|
},
|
||||||
mixer: {
|
mixer: {
|
||||||
id: "mixer",
|
id: "mixer",
|
||||||
button: "gray",
|
button: "gray",
|
||||||
},
|
},
|
||||||
|
guess: {
|
||||||
|
id: "guess",
|
||||||
|
button: "cyan",
|
||||||
|
},
|
||||||
gallery: {
|
gallery: {
|
||||||
id: "gallery",
|
id: "gallery",
|
||||||
button: "gold",
|
button: "gold",
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ export interface Props {
|
|||||||
noFooter?: boolean;
|
noFooter?: boolean;
|
||||||
noNavbar?: boolean;
|
noNavbar?: boolean;
|
||||||
integrated?: boolean;
|
integrated?: boolean;
|
||||||
|
locale: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const servicing = import.meta.env.SERVICING;
|
const servicing = import.meta.env.SERVICING;
|
||||||
const { noFooter, noNavbar, integrated } = Astro.props;
|
const { noFooter, noNavbar, integrated, locale } = Astro.props;
|
||||||
const locale = Astro.params.locale;
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang={locale}>
|
<html lang={locale}>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import BaseLayout from "./BaseLayout.astro";
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tool: Tool;
|
tool: Tool;
|
||||||
|
locale: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tool } = Astro.props;
|
const { tool, locale } = Astro.props;
|
||||||
const { locale } = Astro.params;
|
|
||||||
const strings = getStrings(locale);
|
const strings = getStrings(locale);
|
||||||
const toolStrings =
|
const toolStrings =
|
||||||
strings.website.tools[
|
strings.website.tools[
|
||||||
@@ -20,7 +20,7 @@ const toolStrings =
|
|||||||
|
|
||||||
<ToolOpenGraph {tool} />
|
<ToolOpenGraph {tool} />
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout {locale}>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url(/assets/images/tools/${tool.id}.webp)`,
|
backgroundImage: `url(/assets/images/tools/${tool.id}.webp)`,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const { locale } = Astro.params;
|
|||||||
|
|
||||||
<ToolOpenGraph tool={tools.performance} />
|
<ToolOpenGraph tool={tools.performance} />
|
||||||
|
|
||||||
<BaseLayout noFooter>
|
<BaseLayout {locale} noFooter>
|
||||||
<Page client:only="react" {locale}>
|
<Page client:only="react" {locale}>
|
||||||
<Page skeleton slot="fallback" {locale} />
|
<Page skeleton slot="fallback" {locale} />
|
||||||
</Page>
|
</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