feat: better landing screen

This commit is contained in:
TrèsAbhi committed 2025-10-12 22:48:14 -05:00
1 parent bfceacdf3c
commit ef173758aa
5 files changed
+113 -112

No files matched your search

+1
View File
@@ -860,6 +860,7 @@
"tools": { "tools": {
"playlist": { "playlist": {
"clear": "Clear",
"table": { "table": {
"tank": "Tank" "tank": "Tank"
}, },
@@ -1,86 +0,0 @@
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 { LinkI18n } from "./LinkI18n";
import { FilterControl } from "./TankSearch/components/FilterControl";
const tankDefinitions = await awaitableTankDefinitions;
export function PlaylistPool() {
const { strings, locale } = 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) => (
<LinkI18n
target="_blank"
locale={locale}
href={`/tanks/${tank.slug}`}
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))",
pointerEvents: "none",
}}
/>
</Box>
</Flex>
);
}
@@ -1,34 +1,112 @@
import { tankIcon } from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
import { Button, Flex, Text } from "@radix-ui/themes"; import { Box, Button, 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 { generatePlaylist } from "../core/blitzkit/generatePlaylist"; import { generatePlaylist } from "../core/blitzkit/generatePlaylist";
import { useLocale } from "../hooks/useLocale"; import { useLocale } from "../hooks/useLocale";
import { App } from "../stores/app"; import { App } from "../stores/app";
import { TankFilters } from "../stores/tankFilters";
import { LinkI18n } from "./LinkI18n";
import { FilterControl } from "./TankSearch/components/FilterControl";
const tankDefinitions = await awaitableTankDefinitions;
const POOL_HEIGHT = "16rem";
export function PlaylistGenerator() { export function PlaylistGenerator() {
const { strings } = useLocale(); const { strings, locale } = useLocale();
const wargaming = App.use((state) => state.logins.wargaming); const wargaming = App.use((state) => state.logins.wargaming);
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, wargaming?.id),
// react-promise-suspense has awful type annotations
[filters] as any
);
const tanksTrimmed = useMemo(() => tanks.slice(0, 65), [filters]);
return ( return (
<Flex <Flex justify="center" width="100%" gap="9" align="center">
flexGrow="1" <Flex direction="column" gap="4" align="center">
flexBasis="1" <FilterControl />
direction="column"
align="center" <Button onClick={generatePlaylist} size="3">
justify="center" {strings.website.tools.playlist.generate}
gap="4" </Button>
>
<Button onClick={generatePlaylist} size="3"> {!wargaming && (
{strings.website.tools.playlist.generate} <Text color="gray">
</Button> <Flex align="center" gap="1">
<ExclamationTriangleIcon />
{strings.website.tools.playlist.no_login_warning}
</Flex>
</Text>
)}
</Flex>
<Flex
direction="column"
flexGrow="1"
flexBasis="0"
align="center"
gap="4"
>
<Grid
columns="repeat(auto-fill, minmax(4rem, 1fr))"
flow="dense"
overflow="hidden"
position="relative"
maxHeight={POOL_HEIGHT}
width="100%"
>
{tanksTrimmed.map((tank) => (
<LinkI18n
target="_blank"
locale={locale}
href={`/tanks/${tank.slug}`}
key={tank.id}
style={{
aspectRatio: "4 / 3",
backgroundSize: "cover",
backgroundPosition: "center",
backgroundImage: `url(${tankIcon(tank.id)})`,
backgroundRepeat: "no-repeat",
}}
/>
))}
<Box
position="absolute"
top="0"
left="0"
width="100%"
height="100%"
style={{
pointerEvents: "none",
background: `linear-gradient(transparent, var(--gray-1) ${POOL_HEIGHT})`,
}}
/>
</Grid>
{!wargaming && (
<Text color="gray"> <Text color="gray">
<Flex align="center" gap="1"> {literals(strings.website.tools.playlist.pool_size, {
<ExclamationTriangleIcon /> count: tanks.length,
{strings.website.tools.playlist.no_login_warning} })}
</Flex>
</Text> </Text>
)} </Flex>
</Flex> </Flex>
); );
} }
@@ -1,16 +1,26 @@
import { Checkbox, Flex, Table } from "@radix-ui/themes"; import { Button, Checkbox, Flex, Table } from "@radix-ui/themes";
import { useLocale } from "../hooks/useLocale"; import { useLocale } from "../hooks/useLocale";
import { Playlist } from "../stores/playlist"; import { Playlist } from "../stores/playlist";
import { TankRowHeaderCell } from "./TankRowHeaderCell"; import { TankRowHeaderCell } from "./TankRowHeaderCell";
export function PlaylistOrder() { export function PlaylistOrder() {
const { unwrap, strings } = useLocale(); const { strings } = useLocale();
const list = Playlist.use((state) => state.list); const list = Playlist.use((state) => state.list);
if (!list) return null; if (!list) return null;
return ( return (
<Flex justify="center" flexGrow="1" flexBasis="0"> <Flex direction="column" gap="4" align="center" flexGrow="1" flexBasis="0">
<Button
onClick={() => {
Playlist.mutate((draft) => {
draft.list = undefined;
});
}}
>
{strings.website.tools.playlist.clear}
</Button>
<Table.Root variant="surface"> <Table.Root variant="surface">
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
@@ -1,8 +1,7 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { PageWrapper } from "../../../components/PageWrapper"; import { PageWrapper } from "../../../components/PageWrapper";
import { PlaylistPool } from "../../../components/PlaylistFilters";
import { PlaylistGenerator } from "../../../components/PlaylistGenerator"; import { PlaylistGenerator } from "../../../components/PlaylistGenerator";
import { PlaylistOrder } from "../../../components/PLaylistOrder"; import { PlaylistOrder } from "../../../components/PlaylistOrder";
import { LocaleProvider } from "../../../hooks/useLocale"; import { LocaleProvider } from "../../../hooks/useLocale";
import { App } from "../../../stores/app"; import { App } from "../../../stores/app";
import { Playlist } from "../../../stores/playlist"; import { Playlist } from "../../../stores/playlist";
@@ -30,8 +29,7 @@ export function Page({
return ( return (
<LocaleProvider locale={locale}> <LocaleProvider locale={locale}>
<PageWrapper color="tomato" direction="row" maxWidth="100rem"> <PageWrapper color="tomato" direction="row">
<PlaylistPool />
{!list && <PlaylistGenerator />} {!list && <PlaylistGenerator />}
{list && <PlaylistOrder />} {list && <PlaylistOrder />}
</PageWrapper> </PageWrapper>