mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: more code splitting
This commit is contained in:
1 parent
5d6e864451
commit
a9497d061d
9 files changed
+442
-412
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*,
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
+5
-45
@@ -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
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export function Party3() {
|
||||
|
||||
Reference in new issue
Block a user