mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 00:14:23 +00:00
fix: let the spam game's inbox flash at most three times a second
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { act } from 'react';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { SpamSiegeGame } from '../spam-siege-game';
|
||||
import { SpamSiegeGame, mayFlash, FLASH_MIN_GAP_MS } from '../spam-siege-game';
|
||||
|
||||
// Echo interpolation values so the shield count is visible in the label.
|
||||
vi.mock('next-intl', () => ({
|
||||
@@ -145,3 +145,20 @@ describe('SpamSiegeGame', () => {
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
// WCAG 2.3.1: nothing flashes more than three times a second, however fast
|
||||
// mail is caught or missed.
|
||||
describe('inbox flash rate', () => {
|
||||
it('lets the inbox flash at most three times a second', () => {
|
||||
expect(FLASH_MIN_GAP_MS).toBeGreaterThanOrEqual(1000 / 3);
|
||||
expect(mayFlash(-Infinity, 0)).toBe(true);
|
||||
expect(mayFlash(1000, 1200)).toBe(false);
|
||||
expect(mayFlash(1000, 1000 + FLASH_MIN_GAP_MS)).toBe(true);
|
||||
let flashes = 0;
|
||||
let last = -Infinity;
|
||||
for (let t = 0; t < 1000; t += 50) {
|
||||
if (mayFlash(last, t)) { flashes++; last = t; }
|
||||
}
|
||||
expect(flashes).toBeLessThanOrEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,14 @@ const MAX_STEP_MS = 50;
|
||||
const EXIT_MS = 400;
|
||||
const POP_MS = 700;
|
||||
const FLASH_MS = 280;
|
||||
// The inbox tints at most three times a second, however fast the catches
|
||||
// and misses come (WCAG 2.3.1, three flashes).
|
||||
export const FLASH_MIN_GAP_MS = 334;
|
||||
|
||||
/** Whether the inbox may flash again, `now` ms after the last flash began at `last`. */
|
||||
export function mayFlash(last: number, now: number): boolean {
|
||||
return now - last >= FLASH_MIN_GAP_MS;
|
||||
}
|
||||
const COMBO_STEP = 5;
|
||||
const COMBO_MAX = 3;
|
||||
const BLOCKED_PENALTY = 15;
|
||||
@@ -149,6 +157,7 @@ function freshGame() {
|
||||
nextId: 0,
|
||||
nextPopId: 0,
|
||||
flashToken: 0,
|
||||
lastFlashAt: -Infinity,
|
||||
elapsed: 0,
|
||||
spawnIn: 250,
|
||||
last: 0,
|
||||
@@ -248,6 +257,9 @@ export function SpamSiegeGame({ onClose }: { onClose: () => void }) {
|
||||
|
||||
const flashInbox = useCallback(
|
||||
(kind: "good" | "bad") => {
|
||||
const now = performance.now();
|
||||
if (!mayFlash(game.current.lastFlashAt, now)) return;
|
||||
game.current.lastFlashAt = now;
|
||||
const token = ++game.current.flashToken;
|
||||
setFlash(kind);
|
||||
later(() => {
|
||||
|
||||
Reference in New Issue
Block a user