Chip service and actions, fixed ChipBankCard with delta popup, sounds, spinner, and arena integration; chip bank migration and tests. Arena diffs are the chip-card mounts and join/leave spinner dispatches. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
69 lines
2 KiB
TypeScript
69 lines
2 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* Chip-bank movement sounds for the fixed Chip Bank card. Independent of the
|
|
* table sound engines (the card lives on both the poker and blackjack pages,
|
|
* outside any table). Best-effort autoplay: if the browser blocks the
|
|
* AudioContext until a gesture, these simply stay silent.
|
|
*/
|
|
|
|
let ctx: AudioContext | null = null;
|
|
|
|
function getCtx(): AudioContext | null {
|
|
if (typeof window === "undefined") return null;
|
|
try {
|
|
if (!ctx) {
|
|
const Ctor =
|
|
window.AudioContext ??
|
|
(window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
|
|
if (!Ctor) return null;
|
|
ctx = new Ctor();
|
|
}
|
|
if (ctx.state === "suspended") {
|
|
void ctx.resume().catch(() => undefined);
|
|
}
|
|
return ctx;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function tone(
|
|
frequency: number,
|
|
nextFrequency: number,
|
|
duration: number,
|
|
delay: number,
|
|
volume = 0.14,
|
|
): void {
|
|
const context = getCtx();
|
|
if (!context) return;
|
|
try {
|
|
const start = context.currentTime + delay;
|
|
const oscillator = context.createOscillator();
|
|
const gain = context.createGain();
|
|
oscillator.type = "sine";
|
|
oscillator.frequency.setValueAtTime(frequency, start);
|
|
oscillator.frequency.exponentialRampToValueAtTime(Math.max(1, nextFrequency), start + duration);
|
|
gain.gain.setValueAtTime(0.0001, start);
|
|
gain.gain.exponentialRampToValueAtTime(volume, start + 0.015);
|
|
gain.gain.exponentialRampToValueAtTime(0.0001, start + duration);
|
|
oscillator.connect(gain);
|
|
gain.connect(context.destination);
|
|
oscillator.start(start);
|
|
oscillator.stop(start + duration + 0.05);
|
|
} catch {
|
|
// Sound is best-effort; never throw from a balance indicator.
|
|
}
|
|
}
|
|
|
|
/** Chips arriving: short rising pair. */
|
|
export function playChipBankUp(): void {
|
|
tone(523, 523, 0.09, 0);
|
|
tone(784, 784, 0.12, 0.09);
|
|
}
|
|
|
|
/** Chips leaving: short falling pair. */
|
|
export function playChipBankDown(): void {
|
|
tone(392, 392, 0.09, 0);
|
|
tone(262, 262, 0.14, 0.09);
|
|
}
|