mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-06 05:07:36 -05:00
Sidenav rework?
This commit is contained in:
parent
88e9df56ee
commit
131fa699fa
|
|
@ -1,4 +1,5 @@
|
|||
.container {
|
||||
min-height: calc(100dvh - var(--layout-nav-height));
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
|
@ -14,20 +15,6 @@
|
|||
min-width: 0;
|
||||
}
|
||||
|
||||
.sideNavContainer {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
top: var(--sticky-top);
|
||||
height: fit-content;
|
||||
margin: var(--s-2-5);
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.sideNavContainer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
padding-block: var(--s-4) var(--s-32);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ export const Main = ({
|
|||
halfWidth,
|
||||
bigger,
|
||||
style,
|
||||
sideNav,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
|
|
@ -53,15 +52,6 @@ export const Main = ({
|
|||
</main>
|
||||
);
|
||||
|
||||
if (sideNav) {
|
||||
return (
|
||||
<div className={styles.containerWithSideNav}>
|
||||
<div className={styles.sideNavContainer}>{sideNav}</div>
|
||||
<div className={styles.mainWrapper}>{mainElement}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={styles.container}>{mainElement}</div>;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
.sideNav {
|
||||
--side-nav-width: 200px;
|
||||
background-color: var(--color-bg-high);
|
||||
min-width: var(--side-nav-width);
|
||||
max-width: var(--side-nav-width);
|
||||
border: 1.5px solid var(--color-border);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--color-bg);
|
||||
min-width: var(--layout-sidenav-width);
|
||||
max-width: var(--layout-sidenav-width);
|
||||
border-right: 1.5px solid var(--color-border);
|
||||
overflow: hidden;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.sideNavTop {
|
||||
height: var(--layout-nav-height);
|
||||
background-color: var(--color-bg);
|
||||
border-bottom: 1.5px solid var(--color-border);
|
||||
}
|
||||
|
||||
.sideNavInner {
|
||||
|
|
@ -15,15 +23,14 @@
|
|||
padding: var(--s-1-5);
|
||||
padding-block-end: var(--s-2);
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - var(--sticky-top) - var(--s-3-5));
|
||||
border-radius: calc(var(--rounded) - var(--s-1-5));
|
||||
height: calc(100dvh - var(--layout-nav-height));
|
||||
}
|
||||
|
||||
.sideNavHeader {
|
||||
color: var(--color-text-high);
|
||||
padding: var(--s-1-5) var(--s-2);
|
||||
margin-inline: calc(-1 * var(--s-1-5));
|
||||
background-color: var(--color-bg);
|
||||
background-color: var(--color-bg-high);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s-2);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export function SideNav({
|
|||
}) {
|
||||
return (
|
||||
<nav className={clsx(styles.sideNav, className)}>
|
||||
<div className={styles.sideNavTop} />
|
||||
<div className={clsx(styles.sideNavInner, "scrollbar")}>{children}</div>
|
||||
</nav>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
.container {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: var(--layout-nav-height);
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1.5px solid var(--color-border);
|
||||
|
|
@ -14,9 +13,8 @@
|
|||
backdrop-filter: var(--backdrop-filter);
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
padding-block: var(--s-2);
|
||||
padding-inline: var(--s-4);
|
||||
position: fixed;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { faker } from "@faker-js/faker";
|
||||
import clsx from "clsx";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
|
@ -6,12 +7,85 @@ import type { RootLoaderData } from "~/root";
|
|||
import type { Breadcrumb, SendouRouteHandle } from "~/utils/remix.server";
|
||||
import { SendouButton } from "../elements/Button";
|
||||
import { Image } from "../Image";
|
||||
import { CalendarIcon } from "../icons/Calendar";
|
||||
import { HamburgerIcon } from "../icons/Hamburger";
|
||||
import { TwitchIcon } from "../icons/Twitch";
|
||||
import { UsersIcon } from "../icons/Users";
|
||||
import { SideNav, SideNavHeader, SideNavLink } from "../SideNav";
|
||||
import { Footer } from "./Footer";
|
||||
import styles from "./index.module.css";
|
||||
import { NavDialog } from "./NavDialog";
|
||||
import { TopRightButtons } from "./TopRightButtons";
|
||||
|
||||
function useTimeFormat() {
|
||||
const formatTime = (date: Date, options: Intl.DateTimeFormatOptions) => {
|
||||
return date.toLocaleTimeString("en-US", options);
|
||||
};
|
||||
return { formatTime };
|
||||
}
|
||||
|
||||
const MOCK_STREAMS = [
|
||||
{
|
||||
id: 3,
|
||||
name: "Paddling Pool 252",
|
||||
imageUrl: faker.image.avatar(),
|
||||
subtitle: "Losers Finals",
|
||||
badge: "LIVE",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: "Splash Go!",
|
||||
imageUrl:
|
||||
"https://liquipedia.net/commons/images/7/73/Splash_Go_allmode.png",
|
||||
subtitle: "Tomorrow, 9:00 AM",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Area Cup",
|
||||
imageUrl:
|
||||
"https://pbs.twimg.com/profile_images/1830601967821017088/4SDZVKdj_400x400.jpg",
|
||||
subtitle: "Saturday, 10 AM",
|
||||
},
|
||||
];
|
||||
|
||||
const MOCK_FRIENDS = [
|
||||
{
|
||||
id: 1,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "SendouQ",
|
||||
badge: "2/4",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "Lobby",
|
||||
badge: "2/8",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "In The Zone 22",
|
||||
badge: "3/4",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "SendouQ",
|
||||
badge: "1/4",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "Lobby",
|
||||
badge: "5/8",
|
||||
},
|
||||
];
|
||||
|
||||
function useBreadcrumbs() {
|
||||
const { t } = useTranslation();
|
||||
const matches = useMatches();
|
||||
|
|
@ -45,6 +119,9 @@ export function Layout({
|
|||
const location = useLocation();
|
||||
const breadcrumbs = useBreadcrumbs();
|
||||
|
||||
const { t } = useTranslation(["front"]);
|
||||
const { formatTime } = useTimeFormat();
|
||||
|
||||
const isFrontPage = location.pathname === "/";
|
||||
|
||||
const showLeaderboard =
|
||||
|
|
@ -52,7 +129,7 @@ export function Layout({
|
|||
!data?.user?.roles.includes("MINOR_SUPPORT") &&
|
||||
!location.pathname.includes("plans");
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<>
|
||||
<NavDialog isOpen={navDialogOpen} close={() => setNavDialogOpen(false)} />
|
||||
{isFrontPage ? (
|
||||
<SendouButton
|
||||
|
|
@ -62,35 +139,98 @@ export function Layout({
|
|||
onPress={() => setNavDialogOpen(true)}
|
||||
/>
|
||||
) : null}
|
||||
<header className={clsx(styles.header, styles.itemSize)}>
|
||||
<div className={styles.breadcrumbContainer}>
|
||||
<Link to="/" className={clsx(styles.breadcrumb, styles.logo)}>
|
||||
sendou.ink
|
||||
</Link>
|
||||
{breadcrumbs.flatMap((breadcrumb) => {
|
||||
return [
|
||||
<span
|
||||
key={`${breadcrumb.href}-sep`}
|
||||
className={styles.breadcrumbSeparator}
|
||||
<SideNav>
|
||||
<SideNavHeader icon={<CalendarIcon />}>
|
||||
{t("front:sideNav.myCalendar")}
|
||||
</SideNavHeader>
|
||||
{data.tournaments.participatingFor.length > 0 ? (
|
||||
data.tournaments.participatingFor
|
||||
.slice(0, 4)
|
||||
.map((tournament, index) => (
|
||||
<SideNavLink
|
||||
key={tournament.id}
|
||||
href={tournament.url}
|
||||
imageUrl={tournament.logoUrl ?? undefined}
|
||||
subtitle={`${index < 2 ? "Today" : "Tomorrow"}, ${formatTime(
|
||||
new Date(tournament.startTime),
|
||||
{
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
},
|
||||
)}`}
|
||||
>
|
||||
»
|
||||
</span>,
|
||||
<BreadcrumbLink key={breadcrumb.href} data={breadcrumb} />,
|
||||
];
|
||||
})}
|
||||
</div>
|
||||
<TopRightButtons
|
||||
isErrored={isErrored}
|
||||
showSupport={Boolean(
|
||||
data && !data?.user?.roles.includes("MINOR_SUPPORT") && isFrontPage,
|
||||
)}
|
||||
openNavDialog={() => setNavDialogOpen(true)}
|
||||
/>
|
||||
</header>
|
||||
{showLeaderboard ? <MyRampUnit /> : null}
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
{tournament.name}
|
||||
</SideNavLink>
|
||||
))
|
||||
) : (
|
||||
<div className={styles.sideNavEmpty}>
|
||||
{t("front:sideNav.noEvents")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SideNavHeader icon={<UsersIcon />}>
|
||||
{t("front:sideNav.friends")}
|
||||
</SideNavHeader>
|
||||
{MOCK_FRIENDS.map((friend) => (
|
||||
<SideNavLink
|
||||
key={friend.id}
|
||||
href=""
|
||||
imageUrl={friend.avatarUrl}
|
||||
subtitle={friend.subtitle}
|
||||
badge={friend.badge}
|
||||
>
|
||||
{friend.name}
|
||||
</SideNavLink>
|
||||
))}
|
||||
|
||||
<SideNavHeader icon={<TwitchIcon />}>
|
||||
{t("front:sideNav.streams")}
|
||||
</SideNavHeader>
|
||||
{MOCK_STREAMS.map((stream) => (
|
||||
<SideNavLink
|
||||
key={stream.id}
|
||||
href=""
|
||||
imageUrl={stream.imageUrl}
|
||||
subtitle={stream.subtitle}
|
||||
badge={stream.badge}
|
||||
>
|
||||
{stream.name}
|
||||
</SideNavLink>
|
||||
))}
|
||||
</SideNav>
|
||||
<div className={styles.container}>
|
||||
<header className={clsx(styles.header, styles.itemSize)}>
|
||||
<div className={styles.breadcrumbContainer}>
|
||||
<Link to="/" className={clsx(styles.breadcrumb, styles.logo)}>
|
||||
sendou.ink
|
||||
</Link>
|
||||
{breadcrumbs.flatMap((breadcrumb) => {
|
||||
return [
|
||||
<span
|
||||
key={`${breadcrumb.href}-sep`}
|
||||
className={styles.breadcrumbSeparator}
|
||||
>
|
||||
»
|
||||
</span>,
|
||||
<BreadcrumbLink key={breadcrumb.href} data={breadcrumb} />,
|
||||
];
|
||||
})}
|
||||
</div>
|
||||
<TopRightButtons
|
||||
isErrored={isErrored}
|
||||
showSupport={Boolean(
|
||||
data &&
|
||||
!data?.user?.roles.includes("MINOR_SUPPORT") &&
|
||||
isFrontPage,
|
||||
)}
|
||||
openNavDialog={() => setNavDialogOpen(true)}
|
||||
/>
|
||||
</header>
|
||||
{showLeaderboard ? <MyRampUnit /> : null}
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
import { faker } from "@faker-js/faker";
|
||||
import clsx from "clsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useLoaderData } from "react-router";
|
||||
import { Link } from "react-router";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import { Image } from "~/components/Image";
|
||||
import { CalendarIcon } from "~/components/icons/Calendar";
|
||||
import { TwitchIcon } from "~/components/icons/Twitch";
|
||||
import { UsersIcon } from "~/components/icons/Users";
|
||||
import { Main } from "~/components/Main";
|
||||
import { SideNav, SideNavHeader, SideNavLink } from "~/components/SideNav";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import {
|
||||
BUILDS_PAGE,
|
||||
|
|
@ -552,150 +548,14 @@ function generateMockPosts(): FeedPost[] {
|
|||
|
||||
const MOCK_POSTS = generateMockPosts();
|
||||
|
||||
const MOCK_STREAMS = [
|
||||
{
|
||||
id: 3,
|
||||
name: "Paddling Pool 252",
|
||||
imageUrl: faker.image.avatar(),
|
||||
subtitle: "Losers Finals",
|
||||
badge: "LIVE",
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: "Splash Go!",
|
||||
imageUrl:
|
||||
"https://liquipedia.net/commons/images/7/73/Splash_Go_allmode.png",
|
||||
subtitle: "Tomorrow, 9:00 AM",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Area Cup",
|
||||
imageUrl:
|
||||
"https://pbs.twimg.com/profile_images/1830601967821017088/4SDZVKdj_400x400.jpg",
|
||||
subtitle: "Saturday, 10 AM",
|
||||
},
|
||||
];
|
||||
|
||||
const MOCK_FRIENDS = [
|
||||
{
|
||||
id: 1,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "SendouQ",
|
||||
badge: "2/4",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "Lobby",
|
||||
badge: "2/8",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "In The Zone 22",
|
||||
badge: "3/4",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "SendouQ",
|
||||
badge: "1/4",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: faker.internet.username(),
|
||||
avatarUrl: faker.image.avatar(),
|
||||
subtitle: "Lobby",
|
||||
badge: "5/8",
|
||||
},
|
||||
];
|
||||
|
||||
export default function FrontPage() {
|
||||
const { t } = useTranslation(["front"]);
|
||||
const data = useLoaderData<typeof loader>();
|
||||
const { formatTime } = useTimeFormat();
|
||||
|
||||
return (
|
||||
<Main
|
||||
className={styles.frontPageContainer}
|
||||
sideNav={
|
||||
<SideNav>
|
||||
<SideNavHeader icon={<CalendarIcon />}>
|
||||
{t("front:sideNav.myCalendar")}
|
||||
</SideNavHeader>
|
||||
{data.tournaments.participatingFor.length > 0 ? (
|
||||
data.tournaments.participatingFor
|
||||
.slice(0, 4)
|
||||
.map((tournament, index) => (
|
||||
<SideNavLink
|
||||
key={tournament.id}
|
||||
href={tournament.url}
|
||||
imageUrl={tournament.logoUrl ?? undefined}
|
||||
subtitle={`${index < 2 ? "Today" : "Tomorrow"}, ${formatTime(
|
||||
new Date(tournament.startTime),
|
||||
{
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
},
|
||||
)}`}
|
||||
>
|
||||
{tournament.name}
|
||||
</SideNavLink>
|
||||
))
|
||||
) : (
|
||||
<div className={styles.sideNavEmpty}>
|
||||
{t("front:sideNav.noEvents")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SideNavHeader icon={<UsersIcon />}>
|
||||
{t("front:sideNav.friends")}
|
||||
</SideNavHeader>
|
||||
{MOCK_FRIENDS.map((friend) => (
|
||||
<SideNavLink
|
||||
key={friend.id}
|
||||
href=""
|
||||
imageUrl={friend.avatarUrl}
|
||||
subtitle={friend.subtitle}
|
||||
badge={friend.badge}
|
||||
>
|
||||
{friend.name}
|
||||
</SideNavLink>
|
||||
))}
|
||||
|
||||
<SideNavHeader icon={<TwitchIcon />}>
|
||||
{t("front:sideNav.streams")}
|
||||
</SideNavHeader>
|
||||
{MOCK_STREAMS.map((stream) => (
|
||||
<SideNavLink
|
||||
key={stream.id}
|
||||
href=""
|
||||
imageUrl={stream.imageUrl}
|
||||
subtitle={stream.subtitle}
|
||||
badge={stream.badge}
|
||||
>
|
||||
{stream.name}
|
||||
</SideNavLink>
|
||||
))}
|
||||
</SideNav>
|
||||
}
|
||||
>
|
||||
<Main className={styles.frontPageContainer}>
|
||||
<SocialFeed posts={MOCK_POSTS} />
|
||||
</Main>
|
||||
);
|
||||
}
|
||||
|
||||
function useTimeFormat() {
|
||||
const formatTime = (date: Date, options: Intl.DateTimeFormatOptions) => {
|
||||
return date.toLocaleTimeString("en-US", options);
|
||||
};
|
||||
return { formatTime };
|
||||
}
|
||||
|
||||
function SocialFeed({ posts }: { posts: FeedPost[] }) {
|
||||
return (
|
||||
<div className={styles.socialFeed}>
|
||||
|
|
|
|||
89
app/root.tsx
89
app/root.tsx
|
|
@ -52,6 +52,15 @@ import { i18nCookie, i18next } from "./modules/i18n/i18next.server";
|
|||
import { IS_E2E_TEST_RUN } from "./utils/e2e";
|
||||
import { allI18nNamespaces } from "./utils/i18n";
|
||||
import { isRevalidation, metaTags, type SerializeFrom } from "./utils/remix";
|
||||
import cachified from "@epic-web/cachified";
|
||||
import type { Tables } from "~/db/tables";
|
||||
import * as Changelog from "~/features/front-page/core/Changelog.server";
|
||||
import { cachedFullUserLeaderboard } from "~/features/leaderboards/core/leaderboards.server";
|
||||
import * as LeaderboardRepository from "~/features/leaderboards/LeaderboardRepository.server";
|
||||
import * as Seasons from "~/features/mmr/core/Seasons";
|
||||
import { cache, IN_MILLISECONDS, ttl } from "~/utils/cache.server";
|
||||
import { discordAvatarUrl, teamPage, userPage } from "~/utils/urls";
|
||||
import * as ShowcaseTournaments from "../app/features/front-page/core/ShowcaseTournaments.server";
|
||||
|
||||
export const middleware: Route.MiddlewareFunction[] = [userMiddleware];
|
||||
|
||||
|
|
@ -90,8 +99,25 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|||
const locale = await i18next.getLocale(request);
|
||||
const themeSession = await getThemeSession(request);
|
||||
|
||||
const [tournaments, changelog, leaderboards] = await Promise.all([
|
||||
ShowcaseTournaments.frontPageTournamentsByUserId(user?.id ?? null),
|
||||
cachified({
|
||||
key: "front-changelog",
|
||||
cache,
|
||||
ttl: ttl(IN_MILLISECONDS.ONE_HOUR),
|
||||
staleWhileRevalidate: ttl(IN_MILLISECONDS.TWO_HOURS),
|
||||
async getFreshValue() {
|
||||
return Changelog.get();
|
||||
},
|
||||
}),
|
||||
cachedLeaderboards(),
|
||||
]);
|
||||
|
||||
return data(
|
||||
{
|
||||
tournaments,
|
||||
changelog,
|
||||
leaderboards,
|
||||
locale,
|
||||
theme: themeSession.getTheme(),
|
||||
user: user
|
||||
|
|
@ -121,6 +147,69 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|||
);
|
||||
};
|
||||
|
||||
export interface LeaderboardEntry {
|
||||
name: string;
|
||||
url: string;
|
||||
avatarUrl: string | null;
|
||||
power: number;
|
||||
}
|
||||
|
||||
const ENTRIES_PER_LEADERBOARD = 5;
|
||||
|
||||
function cachedLeaderboards(): Promise<{
|
||||
user: LeaderboardEntry[];
|
||||
team: LeaderboardEntry[];
|
||||
}> {
|
||||
return cachified({
|
||||
key: "front-leaderboard",
|
||||
cache,
|
||||
ttl: ttl(IN_MILLISECONDS.ONE_HOUR),
|
||||
staleWhileRevalidate: ttl(IN_MILLISECONDS.TWO_HOURS),
|
||||
async getFreshValue() {
|
||||
const season = Seasons.currentOrPrevious()?.nth ?? 1;
|
||||
|
||||
const [team, user] = await Promise.all([
|
||||
LeaderboardRepository.teamLeaderboardBySeason({
|
||||
season,
|
||||
onlyOneEntryPerUser: true,
|
||||
}),
|
||||
cachedFullUserLeaderboard(season),
|
||||
]);
|
||||
|
||||
return {
|
||||
user: user.slice(0, ENTRIES_PER_LEADERBOARD).map((entry) => ({
|
||||
power: entry.power,
|
||||
name: entry.username,
|
||||
url: userPage(entry),
|
||||
avatarUrl: entry.discordAvatar
|
||||
? discordAvatarUrl({
|
||||
discordAvatar: entry.discordAvatar,
|
||||
discordId: entry.discordId,
|
||||
size: "sm",
|
||||
})
|
||||
: null,
|
||||
})),
|
||||
team: team
|
||||
.filter((entry) => entry.team)
|
||||
.slice(0, ENTRIES_PER_LEADERBOARD)
|
||||
.map((entry) => {
|
||||
const team = entry.team as Pick<
|
||||
Tables["Team"],
|
||||
"id" | "name" | "customUrl"
|
||||
> & { avatarUrl: string | null };
|
||||
|
||||
return {
|
||||
power: entry.power,
|
||||
name: team.name,
|
||||
url: teamPage(team.customUrl),
|
||||
avatarUrl: team.avatarUrl,
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["common", "game-misc", "weapons"],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ html {
|
|||
|
||||
body {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
line-height: 1.5;
|
||||
overflow-x: hidden;
|
||||
color: var(--color-text);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,9 @@ html {
|
|||
--toggle-height-small: 1.25rem;
|
||||
|
||||
--input-icon-width: 18px;
|
||||
|
||||
--layout-nav-height: 55px;
|
||||
--layout-sidenav-width: 250px;
|
||||
}
|
||||
|
||||
html {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user