@devrongx/games 0.4.15 → 0.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devrongx/games",
3
- "version": "0.4.15",
3
+ "version": "0.4.16",
4
4
  "description": "Game UI components for sports prediction markets",
5
5
  "license": "MIT",
6
6
  "main": "./src/index.ts",
@@ -115,7 +115,6 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
115
115
 
116
116
  // ── Bet state ─────────────────────────────────────────────────────────────
117
117
  const [bets, setBets] = useState<IUserBets>({});
118
- const [expandedPicker, setExpandedPicker] = useState<Record<number, number>>({});
119
118
  const [userFlowState, setUserFlowState] = useState<UserFlowState>("intro");
120
119
  const [showFullLeaderboard, setShowFullLeaderboard] = useState(false);
121
120
  const [submitting, setSubmitting] = useState(false);
@@ -128,7 +127,6 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
128
127
  if (!poolId || !config || !entryData || realBetsRestoredRef.current) return;
129
128
  if (entryData.bets.length === 0) return;
130
129
  const loaded: IUserBets = {};
131
- const pickers: Record<number, number> = {};
132
130
  for (const bet of entryData.bets) {
133
131
  if (bet.parlay_id !== null) continue;
134
132
  const mIdx = config.markets.findIndex((m) => m.backendChallengeId === bet.challenge_id);
@@ -138,12 +136,10 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
138
136
  );
139
137
  if (oIdx < 0) continue;
140
138
  loaded[mIdx] = { optionIdx: oIdx, amount: bet.coin_amount, parlaySlot: null };
141
- pickers[mIdx] = oIdx;
142
139
  }
143
140
  if (Object.keys(loaded).length === 0) return;
144
141
  setBets(loaded);
145
142
  setSubmittedBets(loaded);
146
- setExpandedPicker(pickers);
147
143
  setUserFlowState("game");
148
144
  realBetsRestoredRef.current = true;
149
145
  }, [poolId, config, entryData]);
@@ -172,12 +168,6 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
172
168
  setBets(restored);
173
169
  if (answeredCount >= 5) {
174
170
  // 5+ questions answered — skip straight to game screen
175
- // Expand cards that have no amount set so the picker is visible
176
- const pickers: Record<number, number> = {};
177
- for (const [mIdxStr, entry] of Object.entries(restored)) {
178
- if (entry.amount === 0) pickers[Number(mIdxStr)] = entry.optionIdx;
179
- }
180
- setExpandedPicker(pickers);
181
171
  setUserFlowState("game");
182
172
  } else {
183
173
  // Partial — restore into questions at the first unanswered question
@@ -244,11 +234,6 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
244
234
  // ── Handlers ─────────────────────────────────────────────────────────────
245
235
  const handleQuestionsComplete = useCallback((selections: IUserBets) => {
246
236
  setBets(selections);
247
- const pickers: Record<number, number> = {};
248
- for (const [mIdxStr, entry] of Object.entries(selections)) {
249
- pickers[Number(mIdxStr)] = entry.optionIdx;
250
- }
251
- setExpandedPicker(pickers);
252
237
  setUserFlowState("game");
253
238
  }, []);
254
239
 
@@ -258,10 +243,8 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
258
243
  if (existing?.optionIdx === oIdx) {
259
244
  const next = { ...prev };
260
245
  delete next[mIdx];
261
- setExpandedPicker((p) => { const n = { ...p }; delete n[mIdx]; return n; });
262
246
  return next;
263
247
  }
264
- setExpandedPicker((p) => ({ ...p, [mIdx]: oIdx }));
265
248
  return {
266
249
  ...prev,
267
250
  [mIdx]: { optionIdx: oIdx, amount: existing?.amount ?? 0, parlaySlot: existing?.parlaySlot ?? null },
@@ -364,7 +347,6 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
364
347
  config={config}
365
348
  bets={bets}
366
349
  onBetsChange={setBets}
367
- expandedPicker={expandedPicker}
368
350
  onOptionClick={handleOptionClick}
369
351
  onAmountSelect={handleAmountSelect}
370
352
  betSummary={betSummary}
@@ -1,7 +1,7 @@
1
1
  // @devrongx/games — games/prematch-bets/PreMatchGame.tsx
2
2
  "use client";
3
3
 
4
- import { useState, useEffect, useMemo, useCallback } from "react";
4
+ import { useState, useEffect, useMemo, useCallback, useRef } from "react";
5
5
  import Image from "next/image";
6
6
  import { motion, AnimatePresence, useSpring, useTransform, useMotionValue } from "framer-motion";
7
7
  import { ChevronDown, Info, X, Play, Check, Loader2 } from "lucide-react";
@@ -30,7 +30,6 @@ interface PreMatchGameProps {
30
30
  config: IChallengeConfig;
31
31
  bets: IUserBets;
32
32
  onBetsChange?: (bets: IUserBets) => void;
33
- expandedPicker: Record<number, number>; // mIdx → oIdx for open pickers
34
33
  onOptionClick: (mIdx: number, oIdx: number) => void;
35
34
  onAmountSelect: (mIdx: number, oIdx: number, amount: number) => void;
36
35
  betSummary: IBetSummary;
@@ -50,7 +49,7 @@ interface PreMatchGameProps {
50
49
  }
51
50
 
52
51
 
53
- export const PreMatchGame = ({ config, bets, onBetsChange, expandedPicker, onOptionClick, onAmountSelect, betSummary, leaderboardRows, marketsOnly, onSubmit, submittedBets, onViewLeaderboard, parlayEnabled = false, submitting = false }: PreMatchGameProps) => {
52
+ export const PreMatchGame = ({ config, bets, onBetsChange, onOptionClick, onAmountSelect, betSummary, leaderboardRows, marketsOnly, onSubmit, submittedBets, onViewLeaderboard, parlayEnabled = false, submitting = false }: PreMatchGameProps) => {
54
53
  const { selectedCount, compoundMultiplier, totalEntry, compoundedReward, remainingBalance, riskPercent } = betSummary;
55
54
 
56
55
  // Button state: fresh (no prior submit), saved (submitted, no changes), changed (submitted, user modified)
@@ -87,6 +86,16 @@ export const PreMatchGame = ({ config, bets, onBetsChange, expandedPicker, onOpt
87
86
  });
88
87
  }, []);
89
88
 
89
+ // One-time: when bets are restored with amounts already set, auto-collapse those markets
90
+ const collapseInitializedRef = useRef(false);
91
+ useEffect(() => {
92
+ if (collapseInitializedRef.current) return;
93
+ const withAmounts = Object.entries(bets).filter(([, b]) => b.amount > 0);
94
+ if (withAmounts.length === 0) return;
95
+ setCollapsedMarkets(new Set(withAmounts.map(([k]) => Number(k))));
96
+ collapseInitializedRef.current = true;
97
+ }, [bets]);
98
+
90
99
  // Parlay edit mode: which slot is being edited (null = none)
91
100
  const [editingParlay, setEditingParlay] = useState<number | null>(null);
92
101
  // Parlay info popup: which slot's breakdown is shown (null = none)
@@ -175,7 +184,7 @@ export const PreMatchGame = ({ config, bets, onBetsChange, expandedPicker, onOpt
175
184
  <div className="flex flex-col gap-3">
176
185
  {config.markets.map((market, mIdx) => {
177
186
  const selection = bets[mIdx];
178
- const isPickerOpenForMarket = mIdx in expandedPicker;
187
+ const isPickerOpenForMarket = !collapsedMarkets.has(mIdx) && selection !== undefined;
179
188
  const showCategoryHeader = categoryFirstIndices.has(mIdx);
180
189
 
181
190
  // Available balance for picker: remaining + current bet on this market (it would be freed)
@@ -316,7 +325,7 @@ export const PreMatchGame = ({ config, bets, onBetsChange, expandedPicker, onOpt
316
325
  <div className="grid grid-cols-2 gap-1.5 px-1 pt-0.5 pb-1">
317
326
  {market.options.map((opt, j) => {
318
327
  const isSelected = selection?.optionIdx === j;
319
- const isPickerOpen = isPickerOpenForMarket && expandedPicker[mIdx] === j;
328
+ const isPickerOpen = isPickerOpenForMarket && selection?.optionIdx === j;
320
329
  const showPickerRow = isPickerOpenForMarket;
321
330
  const displayReward = calcDisplayReward(opt, compoundMultiplier, isSelected, selectedCount, isSelected ? selection.amount : undefined);
322
331
  const baseReward = optionReward(opt);
@@ -407,11 +416,11 @@ export const PreMatchGame = ({ config, bets, onBetsChange, expandedPicker, onOpt
407
416
  <div className="flex items-center gap-1.5 overflow-x-auto scrollbar-hide py-1 px-0.5">
408
417
  <span className="text-[8px] text-white/60 font-semibold flex-shrink-0" style={OUTFIT}>Bet:</span>
409
418
  {Array.from({ length: chipCount }, (_, i) => (i + 1) * config.parlayConfig.stakeIncrements).map(amt => {
410
- const isChipSelected = selection?.optionIdx === expandedPicker[mIdx] && selection?.amount === amt;
419
+ const isChipSelected = selection?.amount === amt;
411
420
  return (
412
421
  <div
413
422
  key={amt}
414
- onClick={() => onAmountSelect(mIdx, expandedPicker[mIdx], amt)}
423
+ onClick={() => onAmountSelect(mIdx, selection!.optionIdx, amt)}
415
424
  className="flex-shrink-0 flex items-center gap-0.5 px-2 py-[3px] rounded cursor-pointer transition-colors relative overflow-hidden"
416
425
  style={isChipSelected ? {
417
426
  background: "linear-gradient(135deg, #22E3E8, #9945FF, #f83cc5)",