import { IWheelPrize } from '@nitrots/nitro-renderer'; import { FC, useEffect } from 'react'; import { LocalizeText } from '../../api'; import { renderPrizeIcon } from './wheelPrizeIcon'; import { getPrizeTier } from './wheelPrizeTier'; interface WheelWinRevealProps { prize: IWheelPrize; onDismiss: () => void; } const CONFETTI_COLORS = [ '#ffd34d', '#4fc3f7', '#ff7b7b', '#7bff9e', '#c08bff', '#ffa94d' ]; const CONFETTI_COUNT = 40; // Precomputed once at module load (not during render) so the React Compiler // purity rules stay happy and there's no per-mount cost. A fixed spread of // 40 pieces with staggered delays reads as a lively burst either way. const CONFETTI = Array.from({ length: CONFETTI_COUNT }, (_, i) => ({ left: Math.random() * 100, delay: Math.random() * 0.5, duration: 1.8 + (Math.random() * 1.4), drift: (Math.random() - 0.5) * 160, color: CONFETTI_COLORS[i % CONFETTI_COLORS.length], width: 6 + Math.round(Math.random() * 4) })); // How long each tier lingers before auto-dismissing (ms). const AUTO_DISMISS = { none: 2000, common: 2400, rare: 4000 } as const; export const WheelWinReveal: FC = ({ prize, onDismiss }) => { const tier = getPrizeTier(prize); useEffect(() => { const timer = window.setTimeout(onDismiss, AUTO_DISMISS[tier]); return () => window.clearTimeout(timer); }, [ tier, onDismiss ]); // The "nothing" slice gets a quiet, non-celebratory message. if(tier === 'none') { return (
🍀
{ LocalizeText('wheel.win.nothing') }
); } const isRare = tier === 'rare'; return (
{ isRare && CONFETTI.map((piece, i) => ( )) } { isRare &&
{ LocalizeText('wheel.win.jackpot') }
}
{ renderPrizeIcon(prize, true) }
{ LocalizeText('wheel.win.title') }
{ !!prize.label &&
{ prize.label }
}
); };