mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
fix(coach): spell the bubble's initials from the display name
The person's chat bubble took the first two letters of the login, so a managed record read "MA" for its generated handle and everybody else saw other letters than in the top bar. It now uses nameInitials(accountName) like the top bar, the sidebar and the settings avatar. The thread test that pinned the login's two letters now expects the shared spelling.
This commit is contained in:
1 parent
b5c97062b8
commit
b85989f3df
3 files changed
+59
-7
No files matched your search
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
import { I18nProvider } from "@/lib/i18n/context";
|
||||
|
||||
/**
|
||||
* The person's bubble falls back to initials of the name the app shows, like
|
||||
* the top bar, never the login: a managed record's login is a generated
|
||||
* handle, and "MA" for `managed-…` is nobody's initials.
|
||||
*/
|
||||
const auth = vi.hoisted(() => ({
|
||||
user: {
|
||||
id: "u1",
|
||||
username: "managed-1f2e3d",
|
||||
displayName: "Alex Example" as string | null,
|
||||
role: "USER",
|
||||
avatarUrl: null,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/hooks/use-auth", () => ({
|
||||
useAuth: () => ({ user: auth.user, isAuthenticated: true, isLoading: false }),
|
||||
}));
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useQuery: () => ({ data: undefined }),
|
||||
useMutation: () => ({ mutate: vi.fn(), isPending: false, isError: false }),
|
||||
useQueryClient: () => ({ invalidateQueries: vi.fn() }),
|
||||
}));
|
||||
|
||||
import { ChatBubble } from "../chat-bubble";
|
||||
|
||||
function userBubble(): string {
|
||||
return renderToStaticMarkup(
|
||||
<I18nProvider initialLocale="en">
|
||||
<ChatBubble role="user" content="How did I sleep?" />
|
||||
</I18nProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("coach bubble initials", () => {
|
||||
it("spells the display name, not the login", () => {
|
||||
const html = userBubble();
|
||||
expect(html).toContain(">AE<");
|
||||
expect(html).not.toContain(">MA<");
|
||||
});
|
||||
|
||||
it("falls back to the login when there is no display name", () => {
|
||||
auth.user.displayName = null;
|
||||
auth.user.username = "sam";
|
||||
expect(userBubble()).toContain(">S<");
|
||||
});
|
||||
});
|
||||
@@ -551,10 +551,11 @@ describe("<MessageThread>", () => {
|
||||
});
|
||||
|
||||
it("falls back to initials when no avatar URL is present", () => {
|
||||
// Default mock returns avatarUrl: null → initials path.
|
||||
// Default mock returns avatarUrl: null → initials path, spelled like the
|
||||
// top bar's: one word, one letter.
|
||||
const html = render(<MessageThread conversation={baseConversation} />);
|
||||
expect(html).toContain('data-slot="coach-bubble-user-avatar"');
|
||||
expect(html).toMatch(/data-slot="coach-bubble-user-avatar"[^>]*>TE</);
|
||||
expect(html).toMatch(/data-slot="coach-bubble-user-avatar"[^>]*>T</);
|
||||
});
|
||||
|
||||
// v1.4.25 — optimistic user bubble appears before the "Thinking…"
|
||||
|
||||
@@ -9,6 +9,7 @@ import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { accountName, nameInitials } from "@/lib/account-name";
|
||||
import {
|
||||
parseChartTokens,
|
||||
tokenToMetric,
|
||||
@@ -388,12 +389,10 @@ function ChatBubbleImpl({
|
||||
if (role === "user") {
|
||||
// v1.5.5 — pull the user's self-hosted avatar so the user
|
||||
// bubble matches the Coach avatar in size and visual weight.
|
||||
// Falls back to initials when the user has not uploaded an
|
||||
// avatar. Replaces the Gravatar leak (v1.4.22 B3).
|
||||
// Falls back to the display name's initials, as the top bar does, when
|
||||
// the user has not uploaded an avatar.
|
||||
const avatarUrl = user?.avatarUrl ?? null;
|
||||
const initials = user?.username
|
||||
? user.username.slice(0, 2).toUpperCase()
|
||||
: null;
|
||||
const initials = user ? nameInitials(accountName(user)) || null : null;
|
||||
return (
|
||||
<div
|
||||
data-slot="coach-bubble-user"
|
||||
|
||||
Reference in new issue
Block a user