import Link from "next/link"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import type { CSSProperties } from "react"; import { acceptFriend, declineFriendRequest } from "@/actions/messenger"; import { ContentCard, EmptyState } from "@/components/public/ui"; import { auth } from "@/lib/auth"; import { avatarImageUrl } from "@/lib/format"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; export const dynamic = "force-dynamic"; type SearchParams = Promise<{ accepted?: string; declined?: string; error?: string; }>; function feedbackStyle(tone: "success" | "error"): CSSProperties { const accent = tone === "error" ? "var(--color-danger)" : "var(--color-primary)"; return { margin: 0, padding: "0.85rem 1rem", borderRadius: "var(--radius-md)", border: `1px solid ${accent}`, color: "var(--color-text-readable, var(--color-text))", fontSize: "0.9rem", fontWeight: 600, background: "var(--color-surface)", borderLeft: `4px solid ${accent}`, }; } function formatTimestamp(seconds: number): string { if (!seconds) return ""; // sended_on is a unix timestamp in seconds (emulator convention). return new Date(seconds * 1000).toISOString().slice(0, 16).replace("T", " "); } export default async function MessagesPage({ searchParams, }: { searchParams: SearchParams; }) { const t = await getTranslations("pages.messages"); const session = await auth(); if (!session?.user?.id) redirect("/login"); const userId = Number(session.user.id); const { accepted, declined, error } = await searchParams; // Pending friend requests addressed to the session user, the most recent // offline messages they've received, and the avatar-imager base URL. let requests: { id: number; userFromId: number }[] = []; let messages: { id: number; userFromId: number; message: string; sendedOn: number; }[] = []; let imagerBase = "https://www.habbo.com/habbo-imaging/avatarimage"; try { const [reqRows, msgRows, base] = await Promise.all([ prisma.messengerFriendrequests.findMany({ where: { userToId: userId }, select: { id: true, userFromId: true }, orderBy: { id: "desc" }, take: 100, }), prisma.messengerOffline.findMany({ where: { userId }, select: { id: true, userFromId: true, message: true, sendedOn: true }, orderBy: { sendedOn: "desc" }, take: 50, }), siteSettings.get( "habbo_imaging_url", "https://www.habbo.com/habbo-imaging/avatarimage", ), ]); requests = reqRows; messages = msgRows; if (base) imagerBase = base; } catch { requests = []; messages = []; } // Resolve usernames/looks for every counterparty referenced above. const otherIds = Array.from( new Set([ ...requests.map((r) => r.userFromId), ...messages.map((m) => m.userFromId), ]), ).filter((id) => id && id > 0); let userById = new Map< number, { id: number; username: string; look: string } >(); if (otherIds.length > 0) { try { const users = await prisma.user.findMany({ where: { id: { in: otherIds } }, select: { id: true, username: true, look: true }, }); userById = new Map(users.map((u) => [u.id, u])); } catch { userById = new Map(); } } const errorMessage = error === "not_found" ? t("errors.notFound") : error === "unauthorized" ? t("errors.unauthorized") : error === "invalid" ? t("errors.invalid") : error ? t("errors.error") : null; return (
{accepted === "1" ? (
{t("success.accepted")}
) : null} {declined === "1" ? (
{t("success.declined")}
) : null} {errorMessage ? (
{errorMessage}
) : null} {/* ── Pending friend requests ───────────────────────────── */} {requests.length === 0 ? ( {t("requestsEmptyState")} ) : (
{requests.map((req) => { const from = userById.get(req.userFromId); const avatar = avatarImageUrl(imagerBase, from?.look ?? "", { size: "s", headOnly: true, }); return (
{/* eslint-disable-next-line @next/next/no-img-element */} {`${from?.username

{from ? ( {from.username} ) : ( {t("unknownUser", { id: req.userFromId })} )}

{t("wantsToBeFriend")}

); })}
)}
{/* ── Offline messages ──────────────────────────────────── */} {messages.length === 0 ? ( {t("offlineEmptyState")} ) : (
{messages.map((msg) => { const from = userById.get(msg.userFromId); const avatar = avatarImageUrl(imagerBase, from?.look ?? "", { size: "s", headOnly: true, }); const when = formatTimestamp(msg.sendedOn); return (
{/* eslint-disable-next-line @next/next/no-img-element */} {`${from?.username

{from ? ( {from.username} ) : ( {t("unknownUser", { id: msg.userFromId })} )} {when ? · {when} : null}

{msg.message}

); })}
)}
); }