PURPOSE

Sequential reward popup overlay. Renders the front item from rewardQueueStore as a centered modal popup with source label, icon, and amount/label. Auto-dismisses after 2 seconds, or dismisses immediately on tap/click of the backdrop. Mounted once in Layout.tsx and is always present, becoming visible whenever the queue is non-empty.

OWNS

  • The popup DOM overlay (full-viewport fixed div at zIndex: 9999).
  • The inner popup card (gradient background, border, rounded corners, shadow).
  • The auto-dismiss setTimeout reference stored in timerRef.
  • Inline keyframe animations fadeIn and popIn injected via a <style> tag.
  • The AUTO_DISMISS_MS constant (2000 ms).
  • Visual layout: source label, icon, reward label (with formatted amount), and “tap to continue” hint.

READS FROM

  • useRewardQueueStore — pulls isShowing, current, and dismiss selectors.
  • current() — returns the front reward item (with id, source, icon, amount, label).

PUSHES TO

  • dismiss() on the reward queue store — invoked by the auto-dismiss timer and by backdrop click.

DOES NOT

  • Does not push or enqueue reward items (read-only consumer of the queue).
  • Does not handle multiple items simultaneously (renders only the front item).
  • Does not persist anything or talk to Supabase/network.
  • Does not animate icons or render reward-type-specific visuals beyond the icon string.
  • Does not block input outside the popup beyond the backdrop overlay.
  • Does not call dismiss when clicking on the inner popup card (click propagation is stopped).

Signals

  • Mount/queue-change effect keyed on isShowing and item?.id — sets a fresh auto-dismiss timer per item and clears it on unmount or item swap.
  • Backdrop click handler — calls dismiss().
  • Inner card click handler — stopPropagation to prevent accidental dismiss when interacting with the card.

Entry points

  • RewardPopup() — named React function component export. Takes no props.

Pattern notes

  • Always-mounted overlay pattern: renders null when isShowing is false or no current item exists, so it can sit in Layout.tsx permanently without conditional mounting.
  • Per-item timer reset is achieved by including item?.id in the effect dependency array, so each new front-of-queue item gets its own 2-second window.
  • Inline styles only — no CSS module, no external stylesheet. Keyframes are injected via a child <style> tag inside the overlay.
  • Backdrop-vs-card click separation uses stopPropagation on the inner card, keeping the dismiss-on-backdrop UX while preserving card interactivity.
  • Amount formatting uses toLocaleString() and is prefixed with + only when amount > 0; zero-amount rewards render label only.
  • Font family 'Cal Sans', cursive is used for the source label and reward label.