fix(notifications): a notice's button is said when shown, like its text

An action label was translated when the notice was raised, so after a language switch a live
toast or center row read in one language with its button in the other. The access-requests nudge
no longer re-issues on a switch for it. New tests: the button and a server refusal follow a
switch.
This commit is contained in:
2026-10-30 00:10:38 +01:00
parent 4a0d4b0964
commit b515c16117
26 changed files with 98 additions and 70 deletions
@@ -9,7 +9,7 @@ import { afterEach, describe, expect, it, onTestFinished, vi } from "vitest";
import { act, cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { api, type AppNotification } from "../../lib/api";
import { raw, type Notification as ClientNotif } from "../../lib/notifications";
import { msg, type Notification as ClientNotif, raw } from "../../lib/notifications";
const h = vi.hoisted(() => ({
server: [] as unknown[],
@@ -295,7 +295,7 @@ describe("NotificationsPanel · an activity row", () => {
h.client = [
client(7, {
dismissed: false,
action: { label: "feed.undo", onClick: () => {} },
action: { label: msg("feed.undo"), onClick: () => {} },
meta: { kind: "video-hidden", videoId: "v", channelId: "c" },
} as never),
];
@@ -321,8 +321,8 @@ describe("NotificationsPanel · an activity row", () => {
h.client = [
client(5, {
dismissed: false,
action: { label: "tray.done.copyLink", onClick: copy },
secondaryAction: { label: "tray.done.open", onClick: open },
action: { label: msg("tray.done.copyLink"), onClick: copy },
secondaryAction: { label: msg("tray.done.open"), onClick: open },
}),
];
mount();
@@ -393,7 +393,7 @@ export default function NotificationsPanel() {
: !n.dismissed
? liveActions(n).map((action, i) => ({
id: `action${i}`,
label: action.label,
label: sayNotif(action.label, t),
onSelect: () => {
action.onClick();
dismissClient(n.id);
@@ -570,7 +570,7 @@ export default function NotificationsPanel() {
}
/** A client notice's live actions (they do not survive a reload): its action, then the lesser one. */
function liveActions(n: ClientNotif): { label: string; onClick: () => void }[] {
function liveActions(n: ClientNotif): NonNullable<ClientNotif["action"]>[] {
return [n.action, n.secondaryAction].filter((a) => a !== undefined);
}
@@ -920,7 +920,7 @@ function ClientActivityRow({
<div className="mt-1 flex flex-wrap gap-x-4">
{liveActions(n).map((action, i) => (
<Button
key={action.label}
key={i}
variant="link"
className="text-sm"
// The first is the row's own: Enter on the row runs it.
@@ -930,7 +930,7 @@ function ClientActivityRow({
dismissClient(n.id);
}}
>
{action.label}
{sayNotif(action.label, t)}
</Button>
))}
</div>