UI: gesture & history polish — pinch/swipe fix, wider back-swipe, nudge align, history overscroll
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m10s

- Stop a two-finger pinch-out from also opening the history: the board wrapper arms its
  open/close pull only while a single pointer is down (a 2nd finger is a pinch, owned by Board).
- Widen the edge-swipe-back activation band to the left half of the viewport (was 20%).
- Align a chat nudge by sender like a bubble — your own to the right, the opponent's to the
  left (only the alignment changes).
- Kill the iOS rubber-band inside the history drawer (overscroll-behavior: none).

e2e: a two-finger pinch does not open the history; a back-swipe from the left half navigates back.
This commit is contained in:
Ilia Denisov
2026-06-11 21:01:43 +02:00
parent 6268b9d2a2
commit a41c35d5f9
5 changed files with 82 additions and 9 deletions
+40
View File
@@ -58,6 +58,46 @@ test('the history is a per-seat grid with move scores but no names or running to
await expect(grid).not.toContainText('Ann');
});
test('a two-finger pinch does not open the history (pinch-zoom vs swipe-down)', async ({ page }) => {
await openGame(page);
await page.locator('.stage').evaluate((el) => (el.scrollTop = 0));
const box = (await page.locator('.boardwrap').boundingBox())!;
const cx = box.x + box.width / 2;
const y0 = box.y + 40;
// Two fingers land, then spread apart while their centroid drifts down — a pinch-out whose
// downward component used to also slide the history open. With the single-pointer guard the
// second finger disarms the pull, so the history stays closed.
await page.locator('.boardwrap').evaluate(
(el, { cx, y0 }) => {
const fire = (type: string, id: number, x: number, y: number) =>
el.dispatchEvent(
new PointerEvent(type, {
pointerType: 'touch',
pointerId: id,
isPrimary: id === 1,
clientX: x,
clientY: y,
bubbles: true,
cancelable: true,
}),
);
fire('pointerdown', 1, cx - 20, y0);
fire('pointerdown', 2, cx + 20, y0);
for (let i = 1; i <= 8; i++) {
const d = i * 18;
fire('pointermove', 1, cx - 20 - d, y0 + d);
fire('pointermove', 2, cx + 20 + d, y0 + d);
}
fire('pointerup', 1, cx - 164, y0 + 144);
fire('pointerup', 2, cx + 164, y0 + 144);
},
{ cx, y0 },
);
await expect(page.locator('.history')).toHaveCount(0);
});
test('a swipe-down on the zoomed-in board does not open the history (native scroll wins)', async ({ page }) => {
await openGame(page);
// Double-tap an empty cell to zoom in (two synchronous clicks = a double-tap).