import { AnimatePresence, motion } from "framer-motion"; import { useEffect, useState } from "react"; import { useNfcWs } from "./NfcWsContext"; const SPARKS = 16; const COINS = 7; function playChaChing() { try { const Ctx = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext; if (!Ctx) { return; } const ctx = new Ctx(); const master = ctx.createGain(); master.gain.value = 0.11; master.connect(ctx.destination); const ding = (freq: number, t0: number, dur: number) => { const o = ctx.createOscillator(); const g = ctx.createGain(); o.type = "sine"; o.frequency.setValueAtTime(freq, t0); g.gain.setValueAtTime(0, t0); g.gain.linearRampToValueAtTime(1, t0 + 0.02); g.gain.exponentialRampToValueAtTime(0.01, t0 + dur); o.connect(g); g.connect(master); o.start(t0); o.stop(t0 + dur + 0.05); }; const now = ctx.currentTime; ding(523.25, now, 0.11); ding(659.25, now + 0.055, 0.13); ding(783.99, now + 0.1, 0.16); ding(1046.5, now + 0.14, 0.2); const noise = ctx.createBufferSource(); const buf = ctx.createBuffer(1, ctx.sampleRate * 0.07, ctx.sampleRate); const data = buf.getChannelData(0); for (let i = 0; i < data.length; i++) { data[i] = (Math.random() * 2 - 1) * Math.exp(-i / (data.length * 0.32)); } noise.buffer = buf; const ng = ctx.createGain(); ng.gain.setValueAtTime(0.055, now + 0.04); ng.gain.exponentialRampToValueAtTime(0.001, now + 0.12); noise.connect(ng); ng.connect(master); noise.start(now + 0.04); noise.stop(now + 0.18); void ctx.resume?.(); setTimeout(() => void ctx.close(), 700); } catch { /* autoplay / policy */ } } export default function ScanCashFlourish() { const { cashWave, cashVariant, lastTag } = useNfcWs(); const [visible, setVisible] = useState(false); const [burstKey, setBurstKey] = useState(0); useEffect(() => { if (cashWave === 0) { return; } setBurstKey(cashWave); setVisible(true); playChaChing(); const t = window.setTimeout(() => setVisible(false), 1650); return () => window.clearTimeout(t); }, [cashWave]); return (
{visible ? ( {Array.from({ length: SPARKS }).map((_, i) => { const a = (i / SPARKS) * Math.PI * 2; const dist = 56 + (i % 4) * 10; return ( ); })} {Array.from({ length: COINS }).map((_, i) => ( 🪙 ))} $ {cashVariant === "vault" ? "VAULT · LOCKED" : "CHA-CHING · HIT"} {lastTag?.uid ? ( {lastTag.uid} ) : null} ) : null}
); }