Compare commits
2 Commits
abb6e4235f
...
4c512d323e
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c512d323e | |||
| 7f271ef2e0 |
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 35 KiB |
@@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { getCharacterBySlug } from "@/data/constants/character";
|
||||
import { CharacterProvider } from "@/providers/character-provider";
|
||||
@@ -18,13 +18,27 @@ import { PrivateMessageCard } from "../private-message-card";
|
||||
import { PwaInstallDialog } from "../pwa-install-dialog";
|
||||
import { TextBubble } from "../text-bubble";
|
||||
|
||||
const userMocks = vi.hoisted(() => ({
|
||||
avatarUrl: null as string | null,
|
||||
}));
|
||||
|
||||
vi.mock("@/router/use-app-navigator", () => ({
|
||||
useAppNavigator: () => ({
|
||||
openSubscription: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/stores/user/user-context", () => ({
|
||||
useUserSelector: (
|
||||
selector: (state: { context: { avatarUrl: string | null } }) => unknown,
|
||||
) => selector({ context: { avatarUrl: userMocks.avatarUrl } }),
|
||||
}));
|
||||
|
||||
describe("chat Tailwind components", () => {
|
||||
beforeEach(() => {
|
||||
userMocks.avatarUrl = null;
|
||||
});
|
||||
|
||||
it("renders MessageAvatar AI and user branches with image utilities", () => {
|
||||
const aiHtml = renderWithCharacter(<MessageAvatar isFromAI={true} />);
|
||||
const userHtml = renderWithCharacter(
|
||||
@@ -228,6 +242,7 @@ describe("chat Tailwind components", () => {
|
||||
expect(memberHtml).not.toContain("px-(--spacing-md,12px)");
|
||||
expect(memberHtml).toContain("bg-[rgba(13,11,20,0.7)]");
|
||||
expect(memberHtml).toContain("size-(--responsive-icon-button-size,42px)");
|
||||
expect(memberHtml).toContain("lucide-user-round");
|
||||
expect(memberWithHintHtml).toContain(
|
||||
'data-analytics-key="chat.external_browser_hint"',
|
||||
);
|
||||
@@ -249,6 +264,20 @@ describe("chat Tailwind components", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("renders the user avatar in the ChatHeader Profile action", () => {
|
||||
userMocks.avatarUrl = "/images/avatar/profile-user.png";
|
||||
|
||||
const html = renderWithCharacter(<ChatHeader isGuest={false} />);
|
||||
|
||||
expect(html).toContain("%2Fimages%2Favatar%2Fprofile-user.png");
|
||||
expect(html).toContain('aria-label="Profile"');
|
||||
expect(html).toContain('data-analytics-key="chat.open_profile"');
|
||||
expect(html).toContain(
|
||||
"var(--responsive-icon-button-size, 42px)",
|
||||
);
|
||||
expect(html).not.toContain("lucide-user-round");
|
||||
});
|
||||
|
||||
it("links guest chat back to the active character splash", () => {
|
||||
const maya = getCharacterBySlug("maya");
|
||||
if (!maya) throw new Error("Missing Maya character fixture");
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
/**
|
||||
* ChatHeader 顶部栏
|
||||
*
|
||||
* 图标:lucide-react <Lock />(游客 banner)+ <UserRound />(Profile)
|
||||
* tree-shakable,currentColor 继承父 color
|
||||
* Profile 优先展示用户头像;头像缺失时回退到 lucide-react <UserRound />。
|
||||
*/
|
||||
import type { ReactNode } from "react";
|
||||
import { Lock, UserRound } from "lucide-react";
|
||||
|
||||
import { BackButton } from "@/app/_components";
|
||||
import { BackButton, UserMessageAvatar } from "@/app/_components";
|
||||
import { useActiveCharacterRoutes } from "@/providers/character-provider";
|
||||
import { buildGlobalPageUrl } from "@/router/global-route-context";
|
||||
import { ROUTES } from "@/router/routes";
|
||||
import { useAppNavigator } from "@/router/use-app-navigator";
|
||||
import { useUserSelector } from "@/stores/user/user-context";
|
||||
|
||||
import { BrowserHintOverlay } from "./browser-hint-overlay";
|
||||
|
||||
@@ -29,6 +29,10 @@ export function ChatHeader({
|
||||
}: ChatHeaderProps) {
|
||||
const navigator = useAppNavigator();
|
||||
const characterRoutes = useActiveCharacterRoutes();
|
||||
const avatarUrl = useUserSelector((state) => state.context.avatarUrl);
|
||||
const handleOpenProfile = () => {
|
||||
navigator.push(buildGlobalPageUrl(ROUTES.profile, characterRoutes.chat));
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="flex shrink-0 flex-col items-stretch bg-transparent p-0">
|
||||
@@ -72,20 +76,26 @@ export function ChatHeader({
|
||||
{showBrowserHint ? <BrowserHintOverlay /> : null}
|
||||
</div>
|
||||
|
||||
{avatarUrl ? (
|
||||
<UserMessageAvatar
|
||||
avatarUrl={avatarUrl}
|
||||
size="var(--responsive-icon-button-size, 42px)"
|
||||
actionLabel="Profile"
|
||||
analyticsKey="chat.open_profile"
|
||||
onClick={handleOpenProfile}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
data-analytics-key="chat.open_profile"
|
||||
data-analytics-label="Open profile"
|
||||
className="flex size-(--responsive-icon-button-size,42px) cursor-pointer items-center justify-center rounded-full border border-[rgba(255,255,255,0.12)] bg-[rgba(13,11,20,0.7)] text-(--color-text-primary,#fff) shadow-[0_10px_24px_rgba(0,0,0,0.2)] backdrop-blur-md transition-[background,transform] duration-150 hover:bg-[rgba(28,24,39,0.82)] active:scale-96 focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#f657a0]"
|
||||
onClick={() =>
|
||||
navigator.push(
|
||||
buildGlobalPageUrl(ROUTES.profile, characterRoutes.chat),
|
||||
)
|
||||
}
|
||||
onClick={handleOpenProfile}
|
||||
aria-label="Profile"
|
||||
>
|
||||
<UserRound size={24} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user