diff --git a/src/app/panel/components/Header.tsx b/src/app/panel/components/Header.tsx index b51296e..150611a 100644 --- a/src/app/panel/components/Header.tsx +++ b/src/app/panel/components/Header.tsx @@ -59,14 +59,20 @@ export default function Header({ const isLoading = useUserStore((state) => state.isLoading); const role: UserRole = user?.role ?? "guest"; const badge = ROLE_BADGES[role]; + const shouldRenderPublicEmail = hasPublicUserEmail({ + email: user?.email, + role, + }); const accountLabel = - user?.name ?? user?.username ?? user?.email ?? "Guest user"; + user?.name ?? + user?.username ?? + (shouldRenderPublicEmail ? user?.email : undefined) ?? + "Guest user"; const accountInitial = resolveAccountInitial(accountLabel); const statusBadge = isLoading ? "Syncing" : badge.label; const badgeClasses = isLoading ? "bg-[var(--color-surface-muted)] text-[var(--color-text-subtle)] opacity-70" : badge.className; - const shouldRenderPublicEmail = hasPublicUserEmail(user?.email); return (
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index df38bb1..87fd405 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -58,11 +58,14 @@ export default function Navbar() { const user = useUserStore((state) => state.user); const nav = translations[language].nav; const accountCopy = nav.account; + const shouldRenderPublicEmail = hasPublicUserEmail({ + email: user?.email, + role: user?.role, + }); const accountInitial = user?.username?.charAt(0)?.toUpperCase() ?? - user?.email?.charAt(0)?.toUpperCase() ?? + (shouldRenderPublicEmail ? user?.email?.charAt(0)?.toUpperCase() : null) ?? "?"; - const shouldRenderPublicEmail = hasPublicUserEmail(user?.email); const [accountMenuOpen, setAccountMenuOpen] = useState(false); const accountMenuRef = useRef(null); diff --git a/src/components/UnifiedNavigation.tsx b/src/components/UnifiedNavigation.tsx index 8872384..4902f27 100644 --- a/src/components/UnifiedNavigation.tsx +++ b/src/components/UnifiedNavigation.tsx @@ -41,14 +41,17 @@ export default function UnifiedNavigation() { const user = useUserStore((state) => state.user); const { toggleOpen } = useMoltbotStore(); const nav = translations[language].nav; + const shouldRenderPublicEmail = hasPublicUserEmail({ + email: user?.email, + role: user?.role, + }); const accountInitial = user?.username?.charAt(0)?.toUpperCase() ?? - user?.email?.charAt(0)?.toUpperCase() ?? + (shouldRenderPublicEmail ? user?.email?.charAt(0)?.toUpperCase() : null) ?? "?"; const [accountMenuOpen, setAccountMenuOpen] = useState(false); const accountMenuRef = useRef(null); const isChinese = language === "zh"; - const shouldRenderPublicEmail = hasPublicUserEmail(user?.email); useEffect(() => { if (typeof window === "undefined") return; diff --git a/src/lib/publicUserIdentity.test.ts b/src/lib/publicUserIdentity.test.ts index 1dd88c9..bcc7fdf 100644 --- a/src/lib/publicUserIdentity.test.ts +++ b/src/lib/publicUserIdentity.test.ts @@ -25,7 +25,18 @@ describe("publicUserIdentity", () => { }); it("detects whether a public email should be rendered", () => { - expect(hasPublicUserEmail("")).toBe(false); - expect(hasPublicUserEmail("guest@svc.plus")).toBe(true); + expect(hasPublicUserEmail({ email: "" })).toBe(false); + expect( + hasPublicUserEmail({ + email: "sandbox@svc.plus", + role: "guest", + }), + ).toBe(false); + expect( + hasPublicUserEmail({ + email: "admin@svc.plus", + role: "admin", + }), + ).toBe(true); }); }); diff --git a/src/lib/publicUserIdentity.ts b/src/lib/publicUserIdentity.ts index c4df8f7..f9b0197 100644 --- a/src/lib/publicUserIdentity.ts +++ b/src/lib/publicUserIdentity.ts @@ -2,11 +2,15 @@ function normalizeText(value?: string | null): string { return typeof value === "string" ? value.trim() : ""; } +function normalizeRole(value?: string | null): string { + return normalizeText(value).toLowerCase(); +} + export function resolvePublicUserEmail(input: { email?: string | null; role?: string | null; }): string { - const normalizedRole = normalizeText(input.role).toLowerCase(); + const normalizedRole = normalizeRole(input.role); if (normalizedRole === "guest") { return ""; } @@ -14,6 +18,13 @@ export function resolvePublicUserEmail(input: { return normalizeText(input.email); } -export function hasPublicUserEmail(email?: string | null): boolean { - return normalizeText(email).length > 0; +export function hasPublicUserEmail(input: { + email?: string | null; + role?: string | null; +}): boolean { + if (normalizeRole(input.role) === "guest") { + return false; + } + + return normalizeText(input.email).length > 0; } diff --git a/src/modules/extensions/builtin/user-center/components/UserOverview.tsx b/src/modules/extensions/builtin/user-center/components/UserOverview.tsx index 8822c12..0ac19cc 100644 --- a/src/modules/extensions/builtin/user-center/components/UserOverview.tsx +++ b/src/modules/extensions/builtin/user-center/components/UserOverview.tsx @@ -51,12 +51,16 @@ export default function UserOverview({ hideMfaMainPrompt = false }: UserOverview const logout = useUserStore((state) => state.logout) const [copied, setCopied] = useState(false) const [guestBoundNodeAddress, setGuestBoundNodeAddress] = useState(null) + const shouldRenderPublicEmail = hasPublicUserEmail({ + email: user?.email, + role: user?.role, + }) const displayName = useMemo(() => resolveDisplayName(user), [user]) const uuid = user?.proxyUuid ?? user?.uuid ?? user?.id ?? '—' const vlessUuid = user?.proxyUuid ?? user?.uuid ?? user?.id ?? null const username = user?.username ?? '—' - const email = hasPublicUserEmail(user?.email) ? user?.email : '—' + const email = shouldRenderPublicEmail ? user?.email : '—' const docsUrl = mfaCopy.actions.docsUrl const isGuestSandboxReadOnly = Boolean(user?.isGuest && user?.isReadOnly) const guestUuidExpiresAtText = useMemo(() => {