|
|
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
|
diff --git a/src/app/(site)/login/page.tsx b/src/app/(site)/login/page.tsx
index ef15e431..daa549d0 100644
--- a/src/app/(site)/login/page.tsx
+++ b/src/app/(site)/login/page.tsx
@@ -7,10 +7,10 @@ import { LoginForm } from "@/components/auth/login-form";
import { Clock } from "@/components/clock";
import { LanguageSwitcher } from "@/components/language-switcher";
import { Reveal } from "@/components/motion-reveal";
+import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { cached } from "@/lib/cache";
import { db, User } from "@/lib/db";
-import { avatarImageUrl } from "@/lib/format";
import { resolveHotelName } from "@/lib/hotel-name";
import { captchaConfig } from "@/lib/services/captcha";
import { siteSettings } from "@/lib/services/site-settings";
@@ -227,17 +227,11 @@ export default async function LoginPage() {
"color-mix(in srgb, var(--color-primary) 4%, transparent)",
}}
>
-
-
-
{requests.map((req) => {
const from = userById.get(req.userFromId);
- const avatar = avatarImageUrl(from?.look ?? "", {
- size: "s",
- headOnly: true,
- });
return (
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
@@ -360,10 +352,6 @@ export default async function MessagesPage({
{messages.map((msg) => {
const from = userById.get(msg.userFromId);
- const avatar = avatarImageUrl(from?.look ?? "", {
- size: "s",
- headOnly: true,
- });
const when = formatDate(new Date(msg.sendedOn * 1000));
return (
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
-
-
{rows.map((row, i) => {
- const avatar = avatarImageUrl(row.look, {
- size: "s",
- headOnly: true,
- });
return (
|
|
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
|
diff --git a/src/app/(site)/radio/shouts/page.tsx b/src/app/(site)/radio/shouts/page.tsx
index 882a30b1..d4c74309 100644
--- a/src/app/(site)/radio/shouts/page.tsx
+++ b/src/app/(site)/radio/shouts/page.tsx
@@ -3,9 +3,9 @@ import { getTranslations } from "next-intl/server";
import type { CSSProperties } from "react";
import { postShout } from "@/actions/radio-shouts";
import { ContentCard, EmptyState } from "@/components/public/ui";
+import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
import { auth } from "@/lib/auth";
import { db, RadioShouts, User } from "@/lib/db";
-import { avatarImageUrl } from "@/lib/format";
import { formatDate } from "@/lib/format-date";
type SearchParams = Promise<{ posted?: string; error?: string }>;
@@ -135,12 +135,6 @@ export default async function RadioShoutsPage({
{shouts.map((s) => {
const author = authorById.get(Number(s.userId));
- const authorAvatar = author
- ? avatarImageUrl(author.look, {
- size: "s",
- headOnly: true,
- })
- : null;
return (
- {authorAvatar ? (
- /* eslint-disable-next-line @next/next/no-img-element */
- 
) : null}
diff --git a/src/app/(site)/rankings/page.tsx b/src/app/(site)/rankings/page.tsx
index d1b6eccf..71056cb5 100644
--- a/src/app/(site)/rankings/page.tsx
+++ b/src/app/(site)/rankings/page.tsx
@@ -1,5 +1,4 @@
import { desc } from "drizzle-orm";
-import Image from "next/image";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import {
@@ -8,8 +7,8 @@ import {
OnlineBadge,
RankBadge,
} from "@/components/public/ui";
+import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
import { db, User } from "@/lib/db";
-import { avatarImageUrl } from "@/lib/format";
export const metadata = { title: "Rankings" };
@@ -50,9 +49,6 @@ export default async function RankingsPage() {
) : (
{users.map((u, i) => {
- const avatar = avatarImageUrl(u.look, {
- size: "m",
- });
return (
-
diff --git a/src/app/(site)/register/page.tsx b/src/app/(site)/register/page.tsx
index 5249f642..bb5bdc3f 100644
--- a/src/app/(site)/register/page.tsx
+++ b/src/app/(site)/register/page.tsx
@@ -7,10 +7,10 @@ import { RegisterForm } from "@/components/auth/register-form";
import { Clock } from "@/components/clock";
import { LanguageSwitcher } from "@/components/language-switcher";
import { Reveal } from "@/components/motion-reveal";
+import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { cached } from "@/lib/cache";
import { db, User } from "@/lib/db";
-import { avatarImageUrl } from "@/lib/format";
import { resolveHotelName } from "@/lib/hotel-name";
import { captchaConfig } from "@/lib/services/captcha";
import { siteSettings } from "@/lib/services/site-settings";
@@ -224,17 +224,11 @@ export default async function RegisterPage() {
"color-mix(in srgb, var(--color-primary) 4%, transparent)",
}}
>
-
-
-
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
+
{guestbook.map((g) => {
const author = authorById.get(g.userId);
- const authorAvatar = author
- ? avatarImageUrl(author.look, {
- size: "s",
- headOnly: true,
- })
- : null;
return (
- {authorAvatar ? (
- /* eslint-disable-next-line @next/next/no-img-element */
- 
) : null}
diff --git a/src/components/shared/public-avatar-contract.test.ts b/src/components/shared/public-avatar-contract.test.ts
new file mode 100644
index 00000000..8d2563ed
--- /dev/null
+++ b/src/components/shared/public-avatar-contract.test.ts
@@ -0,0 +1,46 @@
+import { readFileSync } from "node:fs";
+import { resolve } from "node:path";
+import { describe, expect, it } from "vitest";
+
+const compactCallSites = new Map([
+ ["src/components/top-header.tsx", 2],
+ ["src/app/(site)/friends/page.tsx", 1],
+ ["src/app/(site)/guilds/[id]/page.tsx", 1],
+ ["src/app/(site)/leaderboard/page.tsx", 1],
+ ["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)/radio/leaderboard/page.tsx", 1],
+ ["src/app/(site)/radio/shouts/page.tsx", 1],
+ ["src/app/(site)/rankings/page.tsx", 1],
+ ["src/app/(site)/register/page.tsx", 2],
+ ["src/app/(site)/search/page.tsx", 1],
+ ["src/app/(site)/u/[username]/page.tsx", 2],
+]);
+
+function source(path: string): string {
+ return readFileSync(resolve(process.cwd(), path), "utf8");
+}
+
+describe("public avatar presentation contract", () => {
+ it.each([...compactCallSites])(
+ "uses the shared thumbnail in %s",
+ (path, expectedCount) => {
+ const matches = source(path).match(/ {
+ expect(source("src/app/(site)/me/page.tsx")).toContain(
+ "avatarImageUrl(user.look",
+ );
+ expect(source("src/app/(site)/settings/page.tsx")).toContain(
+ "avatarImageUrl(user.look",
+ );
+ expect(source("src/app/(site)/u/[username]/page.tsx")).toContain(
+ "avatarImageUrl(user.look",
+ );
+ });
+});
diff --git a/src/components/top-header.tsx b/src/components/top-header.tsx
index 569e9bd2..f8df6397 100644
--- a/src/components/top-header.tsx
+++ b/src/components/top-header.tsx
@@ -4,6 +4,7 @@ import Link from "next/link";
import type { Session } from "next-auth";
import { getTranslations } from "next-intl/server";
import { RoomQuickEntry } from "@/components/room-quick-entry";
+import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
import { signOut } from "@/lib/auth";
import { cached } from "@/lib/cache";
import {
@@ -13,7 +14,6 @@ import {
User,
UsersCurrency,
} from "@/lib/db";
-import { avatarImageUrl } from "@/lib/format";
import { resolveHotelName } from "@/lib/hotel-name";
import { canAccess, getApiAdminContext, PERMS } from "@/lib/permissions";
@@ -77,7 +77,6 @@ export async function TopHeader({ session }: { session: Session | null }) {
}
} catch {}
- const avatar = avatarImageUrl(look, { headOnly: true, direction: 2 });
let showAdmin = false;
let showMod = false;
const ctx = await getApiAdminContext();
@@ -318,16 +317,13 @@ export async function TopHeader({ session }: { session: Session | null }) {
aria-label="User menu"
className="list-none cursor-pointer flex items-center [&::-webkit-details-marker]:hidden"
>
-
{session.user.name}
@@ -387,16 +383,10 @@ export async function TopHeader({ session }: { session: Session | null }) {
href="/messages"
className="flex items-center gap-2 rounded-lg px-2 py-1 text-xs transition-colors hover:bg-white/10"
>
-
{u.username}
| |