mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-26 03:59:53 +01:00
feat: better landing screen
This commit is contained in:
1 parent
bfceacdf3c
commit
ef173758aa
5 files changed
+113
-112
No files matched your search
@@ -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>
|
||||||
|
|||||||
Reference in new issue
Block a user