- }>
- {t("front:sideNav.myCalendar")}
-
- {data.tournaments.participatingFor.length > 0 ? (
- data.tournaments.participatingFor
- .slice(0, 4)
- .map((tournament, index) => (
-
- {tournament.name}
-
- ))
- ) : (
-
- {t("front:sideNav.noEvents")}
-
- )}
-
- }>
- {t("front:sideNav.friends")}
-
- {MOCK_FRIENDS.map((friend) => (
-
- {friend.name}
-
- ))}
-
- }>
- {t("front:sideNav.streams")}
-
- {MOCK_STREAMS.map((stream) => (
-
- {stream.name}
-
- ))}
-
- }
- >
+
);
}
-function useTimeFormat() {
- const formatTime = (date: Date, options: Intl.DateTimeFormatOptions) => {
- return date.toLocaleTimeString("en-US", options);
- };
- return { formatTime };
-}
-
function SocialFeed({ posts }: { posts: FeedPost[] }) {
return (
diff --git a/app/root.tsx b/app/root.tsx
index 0fa0d501d..0ab03f70c 100644
--- a/app/root.tsx
+++ b/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"],
};
diff --git a/app/styles/common.css b/app/styles/common.css
index 244c6c4eb..40b4f8ac3 100644
--- a/app/styles/common.css
+++ b/app/styles/common.css
@@ -13,6 +13,8 @@ html {
body {
width: 100%;
+ min-height: 100vh;
+ display: flex;
line-height: 1.5;
overflow-x: hidden;
color: var(--color-text);
diff --git a/app/styles/vars.css b/app/styles/vars.css
index d4724f333..6b8630b9f 100644
--- a/app/styles/vars.css
+++ b/app/styles/vars.css
@@ -120,6 +120,9 @@ html {
--toggle-height-small: 1.25rem;
--input-icon-width: 18px;
+
+ --layout-nav-height: 55px;
+ --layout-sidenav-width: 250px;
}
html {