From aeff9544815b4a5a41b5768856f582be5aa5aac3 Mon Sep 17 00:00:00 2001 From: Snowykami Date: Mon, 22 Sep 2025 21:07:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=90=8D=E7=A7=B0=E6=A0=BC=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/console/nav-user.tsx | 6 +-- web/src/components/console/site-header.tsx | 7 +-- .../layout/avatar-with-dropdown-menu.tsx | 19 +++++-- web/src/models/user.ts | 8 +-- web/src/utils/common/username.ts | 51 +++++++++++++++++++ 5 files changed, 76 insertions(+), 15 deletions(-) diff --git a/web/src/components/console/nav-user.tsx b/web/src/components/console/nav-user.tsx index dd8c1af..e50b4fd 100644 --- a/web/src/components/console/nav-user.tsx +++ b/web/src/components/console/nav-user.tsx @@ -30,7 +30,7 @@ import { } from "@/components/ui/sidebar" import { getGravatarFromUser } from "@/utils/common/gravatar" -import { getFallbackAvatarFromUsername } from "@/utils/common/username" +import { formatDisplayName, getFallbackAvatarFromUsername } from "@/utils/common/username" import { useAuth } from "@/contexts/auth-context" import { userLogout } from "@/api/user" import { toast } from "sonner" @@ -61,7 +61,7 @@ export function NavUser() { {getFallbackAvatarFromUsername(user.nickname || user.username)}
- {user.nickname}({user.username}) + {formatDisplayName(user)} {user.email} @@ -82,7 +82,7 @@ export function NavUser() { {getFallbackAvatarFromUsername(user.nickname || user.username)}
- {user.nickname}({user.username}) + {formatDisplayName(user)} {user.email} diff --git a/web/src/components/console/site-header.tsx b/web/src/components/console/site-header.tsx index e586039..50f5e98 100644 --- a/web/src/components/console/site-header.tsx +++ b/web/src/components/console/site-header.tsx @@ -1,6 +1,7 @@ import { Button } from "@/components/ui/button" import { Separator } from "@/components/ui/separator" import { SidebarTrigger } from "@/components/ui/sidebar" +import Link from "next/link" export function SiteHeader({ title = "Title" }: { title?: string }) { return ( @@ -14,14 +15,14 @@ export function SiteHeader({ title = "Title" }: { title?: string }) {

{title}

diff --git a/web/src/components/layout/avatar-with-dropdown-menu.tsx b/web/src/components/layout/avatar-with-dropdown-menu.tsx index 44d684a..c0ed32d 100644 --- a/web/src/components/layout/avatar-with-dropdown-menu.tsx +++ b/web/src/components/layout/avatar-with-dropdown-menu.tsx @@ -15,11 +15,11 @@ import { useToLogin } from "@/hooks/use-route"; import { CircleUser } from "lucide-react"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { getGravatarFromUser } from "@/utils/common/gravatar"; -import { getFallbackAvatarFromUsername } from "@/utils/common/username"; +import { formatDisplayName, getFallbackAvatarFromUsername } from "@/utils/common/username"; import { useAuth } from "@/contexts/auth-context"; export function AvatarWithDropdownMenu() { - const {user} = useAuth(); + const { user } = useAuth(); const toLogin = useToLogin(); const handleLogout = () => { @@ -39,8 +39,17 @@ export function AvatarWithDropdownMenu() { : } - - My Account + + + {user &&
+
+ {formatDisplayName(user)} + + {user.email} + +
+
} +
{user && Profile @@ -51,7 +60,7 @@ export function AvatarWithDropdownMenu() { - {user ? `Logout (${user.username})` : "Login"} + {user ? "Logout" : "Login"}
diff --git a/web/src/models/user.ts b/web/src/models/user.ts index d1b975b..764eade 100644 --- a/web/src/models/user.ts +++ b/web/src/models/user.ts @@ -1,12 +1,12 @@ export interface User { id: number; username: string; - nickname: string; - avatarUrl: string; + nickname?: string; + avatarUrl?: string; email: string; - gender: string; + gender?: string; role: string; - language: string; + language?: string; } export enum Role { diff --git a/web/src/utils/common/username.ts b/web/src/utils/common/username.ts index 31a5db1..edb796f 100644 --- a/web/src/utils/common/username.ts +++ b/web/src/utils/common/username.ts @@ -1,5 +1,21 @@ import { User } from "@/models/user"; +/** + * Returns a single-character fallback avatar derived from a username. + * + * Rules: + * - If `username` is falsy (empty string, null, or undefined), returns `"N"`. + * - If the first character is an ASCII letter, returns the uppercase form of that character. + * - Otherwise returns the first character as-is. + * + * @param username - The username to derive the avatar character from. + * @returns A single character to use as a fallback avatar. + * + * @example + * getFallbackAvatarFromUsername("alice"); // "A" + * getFallbackAvatarFromUsername("1user"); // "1" + * getFallbackAvatarFromUsername(""); // "N" + */ export function getFallbackAvatarFromUsername(username: string): string { if (!username) { return "N"; @@ -11,6 +27,24 @@ export function getFallbackAvatarFromUsername(username: string): string { return firstChar; } +/** + * Returns the first-character avatar for a User, preferring nickname over username. + * + * Behavior: + * - If `user.nickname` is present, derives the character from the nickname. + * - Else if `user.username` is present, derives the character from the username. + * - If neither is present, returns `"N"`. + * + * This function delegates the actual character selection logic to `getFallbackAvatarFromUsername`. + * + * @param user - The user object to extract the character from. + * @returns A single character derived from the user's nickname or username, or `"N"` if neither is present. + * + * @example + * getFirstCharFromUser({ nickname: "Bob", username: "bob123" }); // "B" + * getFirstCharFromUser({ username: "1user" }); // "1" + * getFirstCharFromUser({}); // "N" + */ export function getFirstCharFromUser(user: User): string { if (user.nickname) { return getFallbackAvatarFromUsername(user.nickname); @@ -19,4 +53,21 @@ export function getFirstCharFromUser(user: User): string { return getFallbackAvatarFromUsername(user.username); } return "N"; +} + +/** + * Formats a user's display name. + * + * - If the user has a `nickname`, returns `${nickname}(${username})`. + * - Otherwise returns the `username`. + * + * @param user - The user whose display name is to be formatted. + * @returns The formatted display name. If `username` is missing, the returned value may be falsy. + * + * @example + * formatDisplayName({ nickname: "Sam", username: "sam42" }); // "Sam(sam42)" + * formatDisplayName({ username: "sam42" }); // "sam42" + */ +export function formatDisplayName(user: User) :string { + return user?.nickname ? `${user.nickname}(${user.username})` : user.username; } \ No newline at end of file