From 098b675f44799a45d16d78170815fddadf8a3759 Mon Sep 17 00:00:00 2001 From: chenhang Date: Fri, 10 Jul 2026 18:35:17 +0800 Subject: [PATCH] refactor(ui): add shared character avatar --- .../_components/character-avatar.module.css | 15 ++++++ src/app/_components/character-avatar.tsx | 46 +++++++++++++++++++ src/app/_components/index.ts | 1 + .../private-room-screen.module.css | 8 +--- src/app/private-room/private-room-screen.tsx | 8 ++-- .../splash-latest-message.module.css | 3 +- .../components/splash-latest-message.tsx | 10 ++-- src/app/tip/tip-screen.module.css | 8 +--- src/app/tip/tip-screen.tsx | 9 ++-- 9 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 src/app/_components/character-avatar.module.css create mode 100644 src/app/_components/character-avatar.tsx diff --git a/src/app/_components/character-avatar.module.css b/src/app/_components/character-avatar.module.css new file mode 100644 index 00000000..1801bd23 --- /dev/null +++ b/src/app/_components/character-avatar.module.css @@ -0,0 +1,15 @@ +.avatar { + display: inline-flex; + flex: 0 0 auto; + align-items: center; + justify-content: center; + overflow: hidden; + border-radius: 9999px; + background: var(--color-avatar-border, #fbf3f5); +} + +.avatar img { + width: 100%; + height: 100%; + object-fit: cover; +} diff --git a/src/app/_components/character-avatar.tsx b/src/app/_components/character-avatar.tsx new file mode 100644 index 00000000..15992929 --- /dev/null +++ b/src/app/_components/character-avatar.tsx @@ -0,0 +1,46 @@ +"use client"; + +import Image from "next/image"; +import type { CSSProperties } from "react"; + +import styles from "./character-avatar.module.css"; + +export interface CharacterAvatarProps { + src?: string; + alt?: string; + className?: string; + size?: number | string; + imageSize?: number; + priority?: boolean; +} + +export function CharacterAvatar({ + src = "/images/chat/pic-chat-elio.png", + alt = "Elio Silvestri", + className, + size = 43, + imageSize, + priority = false, +}: CharacterAvatarProps) { + const resolvedImageSize = + imageSize ?? (typeof size === "number" ? size : 96); + const style: CSSProperties = { + width: size, + height: size, + }; + + return ( + + {alt} + + ); +} diff --git a/src/app/_components/index.ts b/src/app/_components/index.ts index 29863bfa..edc9714a 100644 --- a/src/app/_components/index.ts +++ b/src/app/_components/index.ts @@ -3,4 +3,5 @@ */ export * from "./back-button"; +export * from "./character-avatar"; export * from "./user-message-avatar"; diff --git a/src/app/private-room/private-room-screen.module.css b/src/app/private-room/private-room-screen.module.css index 54f79309..ad38acca 100644 --- a/src/app/private-room/private-room-screen.module.css +++ b/src/app/private-room/private-room-screen.module.css @@ -95,18 +95,12 @@ flex: 0 0 auto; place-items: center; border: 3px solid rgba(255, 255, 255, 0.92); - border-radius: 24px; + border-radius: 9999px; background: linear-gradient(145deg, #ffb36d, #ff5d95); box-shadow: 0 16px 34px rgba(255, 95, 128, 0.23); overflow: hidden; } -.avatarFrame img { - width: 100%; - height: 100%; - object-fit: cover; -} - .identityText { min-width: 0; } diff --git a/src/app/private-room/private-room-screen.tsx b/src/app/private-room/private-room-screen.tsx index 7b98c4b9..7b1a88ba 100644 --- a/src/app/private-room/private-room-screen.tsx +++ b/src/app/private-room/private-room-screen.tsx @@ -1,9 +1,9 @@ "use client"; import { useMemo } from "react"; -import Image from "next/image"; import { RefreshCw, Sparkles } from "lucide-react"; +import { CharacterAvatar } from "@/app/_components"; import { AppBottomNav, MobileShell } from "@/app/_components/core"; import { ROUTES } from "@/router/routes"; import { useAppNavigator } from "@/router/use-app-navigator"; @@ -93,11 +93,11 @@ export function PrivateRoomScreen() {
- {displayName}
diff --git a/src/app/splash/components/splash-latest-message.module.css b/src/app/splash/components/splash-latest-message.module.css index 492d0a65..ead428af 100644 --- a/src/app/splash/components/splash-latest-message.module.css +++ b/src/app/splash/components/splash-latest-message.module.css @@ -49,7 +49,7 @@ height: 48px; place-items: center; border: 2px solid rgba(255, 255, 255, 0.74); - border-radius: 18px; + border-radius: 9999px; background: linear-gradient(145deg, #ffb36d, #ff5d95); box-shadow: 0 10px 24px rgba(255, 95, 128, 0.24); overflow: hidden; @@ -58,7 +58,6 @@ .avatar { width: 100%; height: 100%; - object-fit: cover; } .copy { diff --git a/src/app/splash/components/splash-latest-message.tsx b/src/app/splash/components/splash-latest-message.tsx index 61525b5f..43223293 100644 --- a/src/app/splash/components/splash-latest-message.tsx +++ b/src/app/splash/components/splash-latest-message.tsx @@ -1,8 +1,9 @@ "use client"; -import Image from "next/image"; import { MessageCircle } from "lucide-react"; +import { CharacterAvatar } from "@/app/_components"; + import styles from "./splash-latest-message.module.css"; export interface SplashLatestMessageProps { @@ -26,11 +27,10 @@ export function SplashLatestMessage({ aria-label="Open latest message from Elio" > - diff --git a/src/app/tip/tip-screen.module.css b/src/app/tip/tip-screen.module.css index a3fed616..300f54cc 100644 --- a/src/app/tip/tip-screen.module.css +++ b/src/app/tip/tip-screen.module.css @@ -121,7 +121,7 @@ height: clamp(88px, 22.222vw, 112px); place-items: center; border: 4px solid rgba(255, 255, 255, 0.9); - border-radius: 30px; + border-radius: 9999px; background: linear-gradient(145deg, #ffbd7c, #ff5d91); box-shadow: 0 20px 44px rgba(255, 98, 133, 0.24), @@ -129,12 +129,6 @@ overflow: hidden; } -.avatarRing img { - width: 100%; - height: 100%; - object-fit: cover; -} - .eyebrow { margin: 16px 0 0; color: #b35d63; diff --git a/src/app/tip/tip-screen.tsx b/src/app/tip/tip-screen.tsx index 19bf35a1..a942ebb4 100644 --- a/src/app/tip/tip-screen.tsx +++ b/src/app/tip/tip-screen.tsx @@ -1,9 +1,9 @@ "use client"; import { useEffect, useMemo, useRef } from "react"; -import Image from "next/image"; import { ArrowLeft, Heart, Sparkles } from "lucide-react"; +import { CharacterAvatar } from "@/app/_components"; import { MobileShell } from "@/app/_components/core"; import { usePaymentOrderLifecycle } from "@/app/_hooks/use-payment-order-lifecycle"; import type { PayChannel } from "@/data/dto/payment"; @@ -165,11 +165,10 @@ export function TipScreen({
- Elio Silvestri