perf: declutter home page by removing duplicated sections and queries

The landing page showed the same information more than once. Drop the second
avatar grid (latest users) and its DB query, move the online users grid into
the left column, remove the register banner card and the bottom join CTA so
registration is only offered once in the hero, and show the online count a
single time in the hero badge instead of also in the stats row. The online
users query now fetches 12 rows instead of 30, saving bandwidth on every
uncached render. The avatar presentation contract test now expects one
thumbnail call site on the home page.
This commit is contained in:
openhands committed 2026-08-29 21:26:30 +02:00
1 parent b59d6c21af
commit a6e69fe00e
2 files changed
+53 -175

No files matched your search

+52 -174
View File
@@ -29,57 +29,43 @@ async function getHotelData() {
siteSettings.get("cms_logo", ""),
]);
const [
online,
users,
rooms,
articles,
recentUsers,
recentPhotos,
latestUsers,
] = await Promise.all([
cached("online_count", 10_000, () =>
db
.select({ total: count() })
.from(User)
.where(eq(User.online, "1"))
.then((rows) => rows[0]?.total ?? 0),
).catch(() => 0),
cached("total_users", 300_000, () =>
db
.select({ total: count() })
.from(User)
.then((rows) => rows[0]?.total ?? 0),
).catch(() => 0),
cached("total_rooms", 300_000, () =>
db
.select({ total: count() })
.from(Rooms)
.then((rows) => rows[0]?.total ?? 0),
).catch(() => 0),
getNewsList(4),
cached("home_online_users", 15_000, () =>
db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.online, "1"))
.limit(30),
).catch(() => []),
cached("home_recent_photos", 60_000, () =>
db
.select({ id: CameraWeb.id, url: CameraWeb.url })
.from(CameraWeb)
.orderBy(desc(CameraWeb.timestamp))
.limit(4),
).catch(() => []),
cached("home_latest_users", 60_000, () =>
db
.select({ username: User.username, look: User.look })
.from(User)
.orderBy(desc(User.accountCreated))
.limit(8),
).catch(() => []),
]);
const [online, users, rooms, articles, recentUsers, recentPhotos] =
await Promise.all([
cached("online_count", 10_000, () =>
db
.select({ total: count() })
.from(User)
.where(eq(User.online, "1"))
.then((rows) => rows[0]?.total ?? 0),
).catch(() => 0),
cached("total_users", 300_000, () =>
db
.select({ total: count() })
.from(User)
.then((rows) => rows[0]?.total ?? 0),
).catch(() => 0),
cached("total_rooms", 300_000, () =>
db
.select({ total: count() })
.from(Rooms)
.then((rows) => rows[0]?.total ?? 0),
).catch(() => 0),
getNewsList(4),
cached("home_online_users", 15_000, () =>
db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.online, "1"))
.limit(12),
).catch(() => []),
cached("home_recent_photos", 60_000, () =>
db
.select({ id: CameraWeb.id, url: CameraWeb.url })
.from(CameraWeb)
.orderBy(desc(CameraWeb.timestamp))
.limit(4),
).catch(() => []),
]);
return {
hotelName,
@@ -89,7 +75,6 @@ async function getHotelData() {
articles,
recentUsers,
recentPhotos,
latestUsers,
logo,
};
}
@@ -108,7 +93,6 @@ export default async function Home() {
articles,
recentUsers,
recentPhotos,
latestUsers,
logo,
} = await getHotelData();
@@ -303,13 +287,8 @@ export default async function Home() {
{/* ── Stats ── */}
<Reveal>
<div className="grid grid-cols-3 gap-4 stagger-children">
<div className="grid grid-cols-2 gap-4 stagger-children mx-auto max-w-2xl">
{[
{
value: online,
label: tp("statsOnline"),
icon: "/assets/images/icons/online-friends.png",
},
{
value: users,
label: tp("statsCitizens"),
@@ -365,7 +344,7 @@ export default async function Home() {
{/* ── Login + News ── */}
<div className="flex flex-col gap-4 md:flex-row">
{/* Left: Login & Register */}
{/* Left: Login & Online users */}
<Reveal className="flex w-full flex-col gap-4 md:w-1/3 md:flex-shrink-0">
<SurfaceCard
title={th("login")}
@@ -382,48 +361,32 @@ export default async function Home() {
/>
</SurfaceCard>
<Link href="/register" className="relative block group">
<SurfaceCard className="transition-all duration-200 group-hover:shadow-lg">
<Image
src="/assets/images/EnterHubbly.png"
alt="Register"
width={300}
height={100}
className="block w-full"
unoptimized
/>
</SurfaceCard>
<span
className="pointer-events-none absolute inset-0 flex items-center text-2xl font-extrabold text-white md:text-3xl"
style={{
marginLeft: "18px",
fontFamily: "var(--font-nunito)",
textShadow: "0 2px 8px rgba(0,0,0,0.6)",
}}
>
{tp("register")}
</span>
</Link>
{latestUsers.length > 0 && (
{recentUsers.length > 0 && (
<SurfaceCard
title={tp("latestUsers", { count: users.toLocaleString() })}
title={tp("recentUsers")}
icon="/assets/images/icons/navigation/goody.png"
actionHref="/community"
actionLabel={tp("allUsers")}
bodyClassName="p-4"
>
<div className="grid grid-cols-4 gap-2">
{latestUsers.map((u) => (
{recentUsers.map((u) => (
<div
key={u.username}
className="flex flex-col items-center gap-1"
className="flex flex-col items-center gap-0.5 rounded-lg px-1 py-1"
style={{
background:
"color-mix(in srgb, var(--color-primary) 5%, transparent)",
}}
>
<UserAvatarThumbnail
figure={u.look}
alt={u.username}
options={{ direction: 3 }}
options={{ direction: 2 }}
className="rounded-md"
/>
<span
className="max-w-[64px] truncate text-center text-[11px] font-bold leading-tight"
className="max-w-[64px] truncate text-center text-[10px] font-bold leading-tight"
style={{ color: "var(--color-text-readable)" }}
>
{u.username}
@@ -501,91 +464,6 @@ export default async function Home() {
</Reveal>
</div>
{/* ── Online users ── */}
{recentUsers.length > 0 && (
<Reveal>
<SurfaceCard
title={tp("recentUsers")}
icon="/assets/images/icons/navigation/goody.png"
actionHref="/community"
actionLabel={tp("allUsers")}
>
<div className="flex flex-wrap justify-center gap-2">
{recentUsers.slice(0, 12).map((u) => (
<div
key={u.username}
className="flex flex-col items-center gap-0.5 rounded-lg px-2 py-1.5 transition-all duration-200 hover:scale-110 hover:shadow-md"
style={{
background:
"color-mix(in srgb, var(--color-primary) 5%, transparent)",
}}
>
<UserAvatarThumbnail
figure={u.look}
alt={u.username}
options={{ direction: 2 }}
className="rounded-md"
/>
<span
className="max-w-[50px] truncate text-center text-[9px] font-bold leading-tight"
style={{ color: "var(--color-text-readable)" }}
>
{u.username}
</span>
</div>
))}
</div>
</SurfaceCard>
</Reveal>
)}
{/* ── Bottom CTA ── */}
<Reveal>
<div
className="relative overflow-hidden rounded-3xl border px-6 py-12 text-center"
style={{
background: `linear-gradient(135deg, color-mix(in srgb, var(--color-primary) 8%, var(--color-surface)), color-mix(in srgb, var(--color-surface) 70%, transparent))`,
borderColor:
"color-mix(in srgb, var(--color-primary) 15%, transparent)",
}}
>
<h2
className="mb-2 text-2xl font-black tracking-tight md:text-3xl"
style={{
color: "var(--color-text-readable)",
fontFamily: "var(--font-nunito)",
}}
>
{tp("joinNow", { hotelName })}
</h2>
<p
className="mb-6 max-w-sm mx-auto text-sm"
style={{ color: "var(--color-text-muted)" }}
>
{tp("welcomeBody")}
</p>
<Link
href="/register"
className="inline-flex items-center gap-2.5 font-extrabold text-sm px-8 py-3.5 rounded-xl transition-all duration-200 hover:scale-[1.03] active:scale-95 shadow-lg"
style={{
background: "var(--color-primary)",
color: "var(--color-primary-foreground)",
boxShadow:
"0 4px 24px color-mix(in srgb, var(--color-primary) 35%, transparent)",
}}
>
<Image
src="/assets/images/EnterHubbly.png"
alt=""
width={20}
height={20}
unoptimized
/>
{tp("register")}
</Link>
</div>
</Reveal>
{/* ── Recent photos ── */}
{recentPhotos.length > 0 && (
<Reveal>
@@ -10,7 +10,7 @@ const compactCallSites = new Map<string, number>([
["src/app/(site)/login/page.tsx", 2],
["src/app/(site)/me/page.tsx", 1],
["src/app/(site)/messages/page.tsx", 2],
["src/app/(site)/page.tsx", 2],
["src/app/(site)/page.tsx", 1],
["src/app/(site)/radio/leaderboard/page.tsx", 1],
["src/app/(site)/radio/shouts/page.tsx", 1],
["src/app/(site)/rankings/page.tsx", 1],