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:
1 parent
b59d6c21af
commit
a6e69fe00e
2 files changed
+53
-175
No files matched your search
+52
-174
@@ -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],
|
||||
|
||||
Reference in new issue
Block a user