import { count, eq, inArray } from "drizzle-orm"; import Image from "next/image"; import type { Session } from "next-auth"; import { getTranslations } from "next-intl/server"; import { signOutAndRevokeTicket } from "@/actions/sessions"; import { HeaderUserIdentity } from "@/components/header-user-identity"; import Link from "@/components/link"; import { LiveOnlineCount } from "@/components/live-online-count"; import { RoomQuickEntry } from "@/components/room-quick-entry"; import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail"; import { db, MessengerFriendrequests, MessengerOffline, User, UsersCurrency, } from "@/lib/db"; import { resolveHotelName } from "@/lib/hotel-name"; import { canAccess, getApiAdminContext, PERMS } from "@/lib/permissions"; import { logServerError } from "@/lib/server-log"; import { cachedOnlineCount } from "@/lib/services/public-counters"; function Currency({ icon, amount, label, }: { icon: string; amount: number; label: string; }) { const textColor = "var(--color-navbar-text-readable, var(--color-navbar-text))"; const valueColor = "var(--color-navbar-text-readable, var(--color-navbar-text, var(--color-text)))"; return (
{amount.toLocaleString()} {" "} {label}
); } export async function TopHeader({ session }: { session: Session | null }) { if (!session?.user?.id) return null; const notificationText = await getTranslations("pages.notifications"); const id = Number(session.user.id); let credits = 0; let duckets = 0; let diamonds = 0; let look = ""; let showAdmin = false; let showMod = false; let hotelName = ""; let online = 0; let friendRequests: { userFromId: number }[] = []; let friendRequestUsers: { id: number; username: string; look: string; }[] = []; // One round of queries instead of five sequential ones: the header is on // every authenticated page, so each extra round-trip is paid on every view. const [wallet, ctx, name, onlineCount, requests] = await Promise.all([ (async () => { try { const [user, currencies] = await Promise.all([ db .select({ credits: User.credits, look: User.look }) .from(User) .where(eq(User.id, id)) .limit(1), db .select({ type: UsersCurrency.type, amount: UsersCurrency.amount }) .from(UsersCurrency) .where(eq(UsersCurrency.userId, id)), ]); return user[0] ? { credits: user[0].credits, look: user[0].look, currencies } : null; } catch (error) { logServerError("top-header.wallet_failed", error, { userId: id }); return null; } })(), getApiAdminContext().catch(() => null), resolveHotelName().catch(() => ""), cachedOnlineCount().catch((error) => { logServerError("top-header.online_count_failed", error); return 0; }), db .select({ userFromId: MessengerFriendrequests.userFromId }) .from(MessengerFriendrequests) .where(eq(MessengerFriendrequests.userToId, id)) .catch(() => [] as { userFromId: number }[]), ]); if (wallet) { credits = wallet.credits; look = wallet.look; for (const c of wallet.currencies) { if (c.type === 0) duckets = c.amount; if (c.type === 5) diamonds = c.amount; } } if (ctx) { showAdmin = canAccess( ctx.permissions, PERMS.ADMIN_DASHBOARD, ctx.session.user.rank, ); showMod = canAccess(ctx.permissions, PERMS.MOD_DASHBOARD, ctx.session.user.rank) || canAccess( ctx.permissions, PERMS.MODERATION_VIEW, ctx.session.user.rank, ) || canAccess(ctx.permissions, PERMS.MOD_CFH_VIEW, ctx.session.user.rank) || canAccess(ctx.permissions, PERMS.MOD_ACTIONS, ctx.session.user.rank); } hotelName = name; online = onlineCount; friendRequests = requests; if (friendRequests.length > 0) { friendRequestUsers = await db .select({ id: User.id, username: User.username, look: User.look }) .from(User) .where( inArray( User.id, friendRequests.map((r) => r.userFromId), ), ) .catch(() => [] as { id: number; username: string; look: string }[]); } // The count and the rows are the same question, so derive it from the rows. const pendingFriendRequests = friendRequests.length; const textColor = "var(--color-navbar-text-readable, var(--color-navbar-text, var(--color-text)))"; // Unread offline messages is the only count we still need on its own. let unreadMessages = 0; try { const [row] = await db .select({ total: count() }) .from(MessengerOffline) .where(eq(MessengerOffline.userId, id)); unreadMessages = row?.total ?? 0; } catch (error) { logServerError("top-header.messenger_counts_failed", error, { userId: id }); } const totalNotifications = unreadMessages + pendingFriendRequests; return (
Enter
{totalNotifications > 0 && ( {totalNotifications > 99 ? "99+" : totalNotifications} )} {showAdmin || showMod ? (
Staff Staff
{showMod ? ( Mod panel ) : null} {showAdmin ? ( <> Admin panel Commandocentrum Logs ) : null} Logo generator
) : null}
My dashboard My profile {notificationText("title")} User settings Two-factor Login activity {pendingFriendRequests > 0 && ( <>

Friend requests ({pendingFriendRequests})

{friendRequestUsers.slice(0, 3).map((u) => ( {u.username} ))} {friendRequestUsers.length > 3 && (

+{friendRequestUsers.length - 3} more

)}
)}
); }