Files
erika/app/leaderboard/vc/page.tsx
T

133 lines
4.7 KiB
TypeScript

import type { Metadata } from "next";
import { redirect } from "next/navigation";
import { ChannelSelect } from "@/components/leaderboard/channel-select";
import { LeaderboardBoard } from "@/components/leaderboard/leaderboard-board";
import { LeaderboardError } from "@/components/leaderboard/leaderboard-error";
import { LeaderboardShell } from "@/components/leaderboard/leaderboard-shell";
import { RealtimeRefresh } from "@/components/realtime-refresh";
import { getVoiceLeaderboardPage } from "@/lib/leaderboard/database";
import { getCurrentDiscordVoiceChannels } from "@/lib/leaderboard/discord";
import { normalizeLeaderboardSearch, normalizeRequestedPage } from "@/lib/leaderboard/ranking";
export const dynamic = "force-dynamic";
export const metadata: Metadata = {
title: "อันดับเวลาในห้องเสียง",
description: "ดูอันดับเวลารวมในห้องเสียงและห้องเวที Discord ปัจจุบันของ Erika!",
alternates: {
canonical: "/leaderboard/vc",
},
};
type VoiceLeaderboardPageProps = {
searchParams: Promise<{
channel?: string | string[];
page?: string | string[];
q?: string | string[];
}>;
};
function voiceLeaderboardUrl(page: number, channelId?: string, searchQuery?: string) {
const params = new URLSearchParams();
if (channelId) params.set("channel", channelId);
if (searchQuery) params.set("q", searchQuery);
if (page > 1) params.set("page", String(page));
const query = params.toString();
return query ? `/leaderboard/vc?${query}` : "/leaderboard/vc";
}
export default async function VoiceLeaderboardPage({
searchParams,
}: VoiceLeaderboardPageProps) {
const query = await searchParams;
const requestedPage = normalizeRequestedPage(query.page);
const searchQuery = normalizeLeaderboardSearch(query.q);
let channels;
try {
channels = await getCurrentDiscordVoiceChannels();
} catch (error) {
console.error("Failed to load Discord voice channels", error);
return (
<LeaderboardShell
active="vc"
title="อันดับเวลาในห้องเสียง"
description="รวมเวลาจากห้องเสียงและห้องเวทีที่ยังอยู่ใน Discord ตอนนี้ หรือเลือกดูเฉพาะห้องได้"
searchQuery={searchQuery}
>
<LeaderboardError source="discord" />
</LeaderboardShell>
);
}
const requestedChannelId = typeof query.channel === "string"
? query.channel
: undefined;
const selectedChannelId = channels.some(
(channel) => channel.id === requestedChannelId,
)
? requestedChannelId
: undefined;
const invalidChannel = query.channel !== undefined && !selectedChannelId;
let data;
try {
data = await getVoiceLeaderboardPage(
channels.map((channel) => channel.id),
requestedPage,
selectedChannelId,
searchQuery,
);
} catch (error) {
console.error("Failed to load voice leaderboard", error);
return (
<LeaderboardShell
active="vc"
title="อันดับเวลาในห้องเสียง"
description="รวมเวลาจากห้องเสียงและห้องเวทีที่ยังอยู่ใน Discord ตอนนี้ หรือเลือกดูเฉพาะห้องได้"
searchQuery={searchQuery}
>
<ChannelSelect
channels={channels}
selectedChannelId={selectedChannelId}
searchQuery={searchQuery}
/>
<LeaderboardError source="database" />
</LeaderboardShell>
);
}
if (
invalidChannel
|| (query.page !== undefined && query.page !== String(data.page))
) {
redirect(voiceLeaderboardUrl(data.page, selectedChannelId, searchQuery));
}
return (
<LeaderboardShell
active="vc"
title="อันดับเวลาในห้องเสียง"
description="รวมเวลาจากห้องเสียงและห้องเวทีที่ยังอยู่ใน Discord ตอนนี้ หรือเลือกดูเฉพาะห้องได้"
searchQuery={searchQuery}
>
<RealtimeRefresh
topic="leaderboards"
leaderboard="vc"
channelId={selectedChannelId}
/>
<ChannelSelect
channels={channels}
selectedChannelId={selectedChannelId}
searchQuery={searchQuery}
/>
<LeaderboardBoard
kind="vc"
data={data}
channelId={selectedChannelId}
searchQuery={searchQuery}
/>
</LeaderboardShell>
);
}