feat: more code splitting

This commit is contained in:
TrèsAbhi committed 2024-05-22 14:16:46 -05:00
1 parent 5d6e864451
commit a9497d061d
9 files changed
+442 -412

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
'use client';
import { AlertDialog, Button, Flex } from '@radix-ui/themes';
import { useEffect, useState } from 'react';
import { extendAuth } from '../../core/blitz/extendAuth';
import { logout } from '../../core/blitz/logout';
import isDev from '../../core/blitzkit/isDev';
import { isLocalhost } from '../../core/blitzkit/isLocalhost';
import { useApp } from '../../stores/app';
const DEV_BUILD_AGREEMENT_COOLDOWN = 8 * 24 * 60 * 60 * 1000;
export function Checks() {
const [showDevBuildAlert, setShowDevBuildAlert] = useState(false);
const login = useApp((state) => state.login);
useEffect(() => {
setShowDevBuildAlert(
isDev() &&
!isLocalhost() &&
Date.now() - useApp.getState().devBuildAgreementTime >=
DEV_BUILD_AGREEMENT_COOLDOWN,
);
}, []);
useEffect(() => {
if (!login) return;
const expiresIn = login.expiresAt - Date.now() / 1000;
const expiresInDays = expiresIn / 60 / 60 / 24;
if (expiresInDays < 0) {
logout();
} else if (expiresInDays < 7) {
extendAuth();
}
});
return (
<AlertDialog.Root open={showDevBuildAlert}>
<AlertDialog.Content>
<AlertDialog.Title>Experimental version!</AlertDialog.Title>
<AlertDialog.Description>
This version may have a lot of issues. Report issues to{' '}
<a href="https://discord.gg/nDt7AjGJQH" target="_blank">
the official Discord server
</a>
. Also consider using{' '}
<a href="https://blitzkit.app/">the more stable release version</a>.
You will be asked again in 8 days.
</AlertDialog.Description>
<Flex justify="end">
<Button
variant="solid"
onClick={() => {
setShowDevBuildAlert(false);
useApp.setState({ devBuildAgreementTime: Date.now() });
}}
>
Continue
</Button>
</Flex>
</AlertDialog.Content>
</AlertDialog.Root>
);
}
+354
View File
@@ -0,0 +1,354 @@
'use client';
import {
CaretRightIcon,
Cross2Icon,
MagnifyingGlassIcon,
} from '@radix-ui/react-icons';
import {
Button,
Card,
Flex,
Heading,
IconButton,
Link,
Spinner,
Text,
TextField,
} from '@radix-ui/themes';
import { go } from 'fuzzysort';
import { debounce } from 'lodash';
import { useRef, useState } from 'react';
import { NAVBAR_HEIGHT } from '../../components/Navbar';
import { UNLOCALIZED_REGION_NAMES_SHORT } from '../../constants/regions';
import searchPlayersAcrossRegions, {
AccountListWithServer,
} from '../../core/blitz/searchPlayersAcrossRegions';
import {
TankDefinition,
tankDefinitions,
tankNames,
} from '../../core/blitzkit/tankDefinitions';
import { TIER_ROMAN_NUMERALS } from '../../core/blitzkit/tankDefinitions/constants';
import { useWideFormat } from '../../hooks/useWideFormat';
import { theme } from '../../stitches.config';
const DISCRIMINATOR_WIDTH = 32;
export function Hero() {
const wideFormat = useWideFormat(512);
const [showSearch, setShowSearch] = useState(false);
const [searching, setSearching] = useState(false);
const [searchResults, setSearchResults] = useState<null | {
tanks: TankDefinition[];
players: AccountListWithServer;
}>();
const search = useRef<HTMLInputElement>(null);
const performSearch = debounce(async () => {
if (!search.current) return;
const sanitized = search.current.value.trim();
const awaitedTankDefinitions = await tankDefinitions;
const awaitedTankNames = await tankNames;
const tanks = go(sanitized, awaitedTankNames, {
keys: ['combined'],
limit: 6,
});
const players = await searchPlayersAcrossRegions(sanitized, 6);
setSearchResults({
tanks: tanks.map((tank) => awaitedTankDefinitions[tank.obj.id]),
players,
});
setSearching(false);
}, 500);
return (
<Flex
align="center"
justify="center"
style={{
height: `calc(75vh - ${NAVBAR_HEIGHT}px)`,
minHeight: 320,
position: 'relative',
}}
p="4"
>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
background: 'url(/assets/banners/home.webp)',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}
/>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
background: `linear-gradient(${theme.colors.appBackground1}80, ${theme.colors.appBackground1}ff)`,
}}
/>
<Flex
direction="column"
gap="4"
align="center"
justify="center"
style={{ position: 'relative', width: '100%' }}
>
<Flex direction="column" align="center">
<Heading size="9" weight="bold" align="center">
BlitzKit
</Heading>
<Text color="gray" align="center">
Everything World of Tanks Blitz
</Text>
</Flex>
<div
style={{
maxWidth: '100%',
position: 'relative',
width: showSearch ? 640 : 320,
transition: 'width 0.2s ease',
}}
>
<TextField.Root
size="3"
placeholder="Search players or tanks..."
style={{
width: '100%',
}}
ref={search}
onChange={() => {
if (!search.current) return;
const sanitized = search.current.value.trim();
if (sanitized.length > 0) {
setShowSearch(true);
setSearching(true);
performSearch();
} else {
setShowSearch(false);
}
}}
onFocus={() => {
if (!search.current) return;
const sanitized = search.current.value.trim();
setShowSearch(sanitized.length > 0);
window.scrollTo({
top:
search.current.getBoundingClientRect().top -
NAVBAR_HEIGHT -
32,
behavior: 'smooth',
});
}}
>
<TextField.Slot>
<MagnifyingGlassIcon />
</TextField.Slot>
{showSearch && (
<TextField.Slot>
<IconButton
variant="ghost"
color="gray"
onClick={() => {
if (!search.current) return;
search.current.value = '';
setShowSearch(false);
}}
>
<Cross2Icon />
</IconButton>
</TextField.Slot>
)}
</TextField.Root>
{showSearch && (
<Card
mt="3"
style={{
position: 'absolute',
top: '100%',
left: '50%',
transform: 'translateX(-50%)',
width: '100%',
}}
>
{searching && (
<Flex justify="center">
<Spinner />
</Flex>
)}
{!searching && searchResults && (
<>
<Flex
gap={wideFormat ? '6' : '2'}
justify="center"
direction={wideFormat ? 'row' : 'column'}
>
<Flex
direction="column"
gap="2"
style={{
flex: 1,
maxWidth: wideFormat ? '50%' : undefined,
}}
>
<Link
underline="hover"
href="/tools/tankopedia"
color="gray"
style={{
width: '100%',
}}
>
<Flex
align="center"
style={{
width: '100%',
}}
>
Tankopedia
<CaretRightIcon />
</Flex>
</Link>
<Flex direction="column" px="2">
{searchResults.tanks.map((tank) => (
<Text>
<Link
underline="none"
style={{ color: 'inherit' }}
href={`/tools/tankopedia/${tank.id}`}
>
<Button
tabIndex={-1}
size="2"
variant="ghost"
color="gray"
style={{
justifyContent: 'flex-start',
width: '100%',
color: 'inherit',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
<Flex align="center" gap="2">
<Text
color="gray"
size="1"
style={{
width: DISCRIMINATOR_WIDTH,
textAlign: 'left',
}}
>
{TIER_ROMAN_NUMERALS[tank.tier]}
</Text>
{tank.name}
</Flex>
</Button>
</Link>
</Text>
))}
{searchResults.tanks.length === 0 && (
<Text color="gray" size="2">
No tanks found
</Text>
)}
</Flex>
</Flex>
<Flex
direction="column"
gap="2"
style={{
flex: 1,
maxWidth: wideFormat ? '50%' : undefined,
}}
>
<Text color="gray">Players</Text>
<Flex direction="column">
{searchResults.players.map((player) => (
<Text>
<Link
underline="none"
style={{ color: 'inherit' }}
href={`/tools/session/?id=${player.account_id}`}
>
<Button
tabIndex={-1}
size="2"
variant="ghost"
color="gray"
style={{
justifyContent: 'flex-start',
width: '100%',
color: 'inherit',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
<Flex align="center" gap="2">
<Text
color="gray"
size="1"
style={{
width: DISCRIMINATOR_WIDTH,
textAlign: 'left',
}}
>
{
UNLOCALIZED_REGION_NAMES_SHORT[
player.region
]
}
</Text>
{player.nickname}
</Flex>
</Button>
</Link>
</Text>
))}
{searchResults.players.length === 0 && (
<Text color="gray" size="2">
No players found
</Text>
)}
</Flex>
</Flex>
</Flex>
</>
)}
</Card>
)}
</div>
</Flex>
</Flex>
);
}
+4
View File
@@ -0,0 +1,4 @@
*,
html {
scroll-behavior: smooth;
}
+5 -45
View File
@@ -1,23 +1,20 @@
'use client';
import { AlertDialog, Button, Flex, Theme } from '@radix-ui/themes';
import { Flex, Theme } from '@radix-ui/themes';
import '@radix-ui/themes/styles.css';
import { config } from 'dotenv';
import { Roboto_Flex } from 'next/font/google';
import { usePathname } from 'next/navigation';
import { ReactNode, Suspense, useEffect, useState } from 'react';
import { ReactNode, Suspense } from 'react';
import { Footer } from '../components/Footer';
import { Loader } from '../components/Loader';
import Navbar, { NAVBAR_HEIGHT } from '../components/Navbar';
import { Party3 } from '../components/Party3';
import isDev from '../core/blitzkit/isDev';
import { isLocalhost } from '../core/blitzkit/isLocalhost';
import { useApp } from '../stores/app';
import { Checks } from './components/Checks';
import './layout.css';
config();
const DEV_BUILD_AGREEMENT_COOLDOWN = 8 * 24 * 60 * 60 * 1000;
interface RootLayoutProps {
children: ReactNode;
}
@@ -31,16 +28,6 @@ export default function RootLayout({ children }: RootLayoutProps) {
const pathname = usePathname();
const isEmbed = pathname.split('/')[1] === 'embeds';
const isRoot = pathname === '/';
const [showDevBuildAlert, setShowDevBuildAlert] = useState(false);
useEffect(() => {
setShowDevBuildAlert(
isDev() &&
!isLocalhost() &&
Date.now() - useApp.getState().devBuildAgreementTime >=
DEV_BUILD_AGREEMENT_COOLDOWN,
);
}, []);
return (
<html lang="en" className={robotoFlex.className}>
@@ -83,34 +70,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
>
{!isEmbed && <Navbar />}
<AlertDialog.Root open={showDevBuildAlert}>
<AlertDialog.Content>
<AlertDialog.Title>Experimental version!</AlertDialog.Title>
<AlertDialog.Description>
This version may have a lot of issues. Report issues to{' '}
<a href="https://discord.gg/nDt7AjGJQH" target="_blank">
the official Discord server
</a>
. Also consider using{' '}
<a href="https://blitzkit.app/">
the more stable release version
</a>
. You will be asked again in 8 days.
</AlertDialog.Description>
<Flex justify="end">
<Button
variant="solid"
onClick={() => {
setShowDevBuildAlert(false);
useApp.setState({ devBuildAgreementTime: Date.now() });
}}
>
Continue
</Button>
</Flex>
</AlertDialog.Content>
</AlertDialog.Root>
<Checks />
<Suspense
fallback={
+3 -349
View File
@@ -1,356 +1,10 @@
'use client';
import {
CaretRightIcon,
Cross2Icon,
MagnifyingGlassIcon,
} from '@radix-ui/react-icons';
import {
Button,
Card,
Flex,
Heading,
IconButton,
Spinner,
Text,
TextField,
} from '@radix-ui/themes';
import { go } from 'fuzzysort';
import { debounce, times } from 'lodash';
import { useRef, useState } from 'react';
import { Link } from '../components/Link';
import { NAVBAR_HEIGHT } from '../components/Navbar';
import { UNLOCALIZED_REGION_NAMES_SHORT } from '../constants/regions';
import searchPlayersAcrossRegions, {
AccountListWithServer,
} from '../core/blitz/searchPlayersAcrossRegions';
import {
TankDefinition,
tankDefinitions,
tankNames,
} from '../core/blitzkit/tankDefinitions';
import { TIER_ROMAN_NUMERALS } from '../core/blitzkit/tankDefinitions/constants';
import { useWideFormat } from '../hooks/useWideFormat';
import { theme } from '../stitches.config';
const DISCRIMINATOR_WIDTH = 32;
import { times } from 'lodash';
import { Hero } from './components/Hero';
export default function Page() {
const wideFormat = useWideFormat(512);
const [showSearch, setShowSearch] = useState(false);
const [searching, setSearching] = useState(false);
const [searchResults, setSearchResults] = useState<null | {
tanks: TankDefinition[];
players: AccountListWithServer;
}>();
const search = useRef<HTMLInputElement>(null);
const performSearch = debounce(async () => {
if (!search.current) return;
const sanitized = search.current.value.trim();
const awaitedTankDefinitions = await tankDefinitions;
const awaitedTankNames = await tankNames;
const tanks = go(sanitized, awaitedTankNames, {
keys: ['combined'],
limit: 6,
});
const players = await searchPlayersAcrossRegions(sanitized, 6);
setSearchResults({
tanks: tanks.map((tank) => awaitedTankDefinitions[tank.obj.id]),
players,
});
setSearching(false);
}, 500);
return (
<>
<Flex
align="center"
justify="center"
style={{
height: `calc(75vh - ${NAVBAR_HEIGHT}px)`,
minHeight: 320,
position: 'relative',
}}
p="4"
>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
background: 'url(/assets/banners/home.webp)',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}
/>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
background: `linear-gradient(${theme.colors.appBackground1}80, ${theme.colors.appBackground1}ff)`,
}}
/>
<Flex
direction="column"
gap="4"
align="center"
justify="center"
style={{ position: 'relative', width: '100%' }}
>
<Flex direction="column" align="center">
<Heading size="9" weight="bold" align="center">
BlitzKit
</Heading>
<Text color="gray" align="center">
Everything World of Tanks Blitz
</Text>
</Flex>
<div
style={{
maxWidth: '100%',
position: 'relative',
width: showSearch ? 640 : 320,
transition: 'width 0.2s ease',
}}
>
<TextField.Root
size="3"
placeholder="Search players or tanks..."
style={{
width: '100%',
}}
ref={search}
onChange={() => {
if (!search.current) return;
const sanitized = search.current.value.trim();
if (sanitized.length > 0) {
setShowSearch(true);
setSearching(true);
performSearch();
} else {
setShowSearch(false);
}
}}
onFocus={() => {
if (!search.current) return;
const sanitized = search.current.value.trim();
setShowSearch(sanitized.length > 0);
window.scrollTo({
top:
search.current.getBoundingClientRect().top -
NAVBAR_HEIGHT -
32,
behavior: 'smooth',
});
}}
>
<TextField.Slot>
<MagnifyingGlassIcon />
</TextField.Slot>
{showSearch && (
<TextField.Slot>
<IconButton
variant="ghost"
color="gray"
onClick={() => {
if (!search.current) return;
search.current.value = '';
setShowSearch(false);
}}
>
<Cross2Icon />
</IconButton>
</TextField.Slot>
)}
</TextField.Root>
{showSearch && (
<Card
mt="3"
style={{
position: 'absolute',
top: '100%',
left: '50%',
transform: 'translateX(-50%)',
width: '100%',
}}
>
{searching && (
<Flex justify="center">
<Spinner />
</Flex>
)}
{!searching && searchResults && (
<>
<Flex
gap={wideFormat ? '6' : '2'}
justify="center"
direction={wideFormat ? 'row' : 'column'}
>
<Flex
direction="column"
gap="2"
style={{
flex: 1,
maxWidth: wideFormat ? '50%' : undefined,
}}
>
<Link
underline="hover"
href="/tools/tankopedia"
color="gray"
style={{
width: '100%',
}}
>
<Flex
align="center"
style={{
width: '100%',
}}
>
Tankopedia
<CaretRightIcon />
</Flex>
</Link>
<Flex direction="column" px="2">
{searchResults.tanks.map((tank) => (
<Text>
<Link
underline="none"
style={{ color: 'inherit' }}
href={`/tools/tankopedia/${tank.id}`}
>
<Button
tabIndex={-1}
size="2"
variant="ghost"
color="gray"
style={{
justifyContent: 'flex-start',
width: '100%',
color: 'inherit',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
<Flex align="center" gap="2">
<Text
color="gray"
size="1"
style={{
width: DISCRIMINATOR_WIDTH,
textAlign: 'left',
}}
>
{TIER_ROMAN_NUMERALS[tank.tier]}
</Text>
{tank.name}
</Flex>
</Button>
</Link>
</Text>
))}
{searchResults.tanks.length === 0 && (
<Text color="gray" size="2">
No tanks found
</Text>
)}
</Flex>
</Flex>
<Flex
direction="column"
gap="2"
style={{
flex: 1,
maxWidth: wideFormat ? '50%' : undefined,
}}
>
<Text color="gray">Players</Text>
<Flex direction="column">
{searchResults.players.map((player) => (
<Text>
<Link
underline="none"
style={{ color: 'inherit' }}
href={`/tools/session/?id=${player.account_id}`}
>
<Button
tabIndex={-1}
size="2"
variant="ghost"
color="gray"
style={{
justifyContent: 'flex-start',
width: '100%',
color: 'inherit',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
<Flex align="center" gap="2">
<Text
color="gray"
size="1"
style={{
width: DISCRIMINATOR_WIDTH,
textAlign: 'left',
}}
>
{
UNLOCALIZED_REGION_NAMES_SHORT[
player.region
]
}
</Text>
{player.nickname}
</Flex>
</Button>
</Link>
</Text>
))}
{searchResults.players.length === 0 && (
<Text color="gray" size="2">
No players found
</Text>
)}
</Flex>
</Flex>
</Flex>
</>
)}
</Card>
)}
</div>
</Flex>
</Flex>
<Hero />
{times(64, () => (
<br />
+2
View File
@@ -1,3 +1,5 @@
'use client';
import { Code, Flex, Link, Spinner, Text, Theme } from '@radix-ui/themes';
import { Suspense } from 'react';
import packageJSON from '../../../package.json';
@@ -33,7 +33,7 @@ export function EverythingSearch({
width: '100%',
zIndex: 1,
}}
></Card>
/>
</TextField.Slot>
)}
</TextField.Root>
+4 -17
View File
@@ -1,3 +1,5 @@
'use client';
import {
Cross1Icon,
DiscordLogoIcon,
@@ -19,13 +21,11 @@ import {
} from '@radix-ui/themes';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Suspense, useEffect, useState } from 'react';
import { Suspense, useState } from 'react';
import * as styles from '../../app/page.css';
import { REGIONS, UNLOCALIZED_REGION_NAMES } from '../../constants/regions';
import { TOOLS } from '../../constants/tools';
import { authURL } from '../../core/blitz/authURL';
import { extendAuth } from '../../core/blitz/extendAuth';
import { logout } from '../../core/blitz/logout';
import { useWideFormat } from '../../hooks/useWideFormat';
import { BlitzkitWide } from '../../icons/BlitzkitWide';
import { PatreonIcon } from '../../icons/Patreon';
@@ -37,22 +37,9 @@ export const NAVBAR_HEIGHT = 64;
export default function Navbar() {
const wideFormat = useWideFormat(688 - 32 - 64);
const login = useApp((state) => state.login);
const [showHamburgerMenu, setShowHamburgerMenu] = useState(false);
const pathname = usePathname();
useEffect(() => {
if (!login) return;
const expiresIn = login.expiresAt - Date.now() / 1000;
const expiresInDays = expiresIn / 60 / 60 / 24;
if (expiresInDays < 0) {
logout();
} else if (expiresInDays < 7) {
extendAuth();
}
});
const login = useApp((state) => state.login!);
return (
<Box
+2
View File
@@ -1,3 +1,5 @@
'use client';
import { useEffect } from 'react';
export function Party3() {