@devrongx/games 0.4.14 → 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.14",
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]);
@@ -238,11 +234,6 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
238
234
  // ── Handlers ─────────────────────────────────────────────────────────────
239
235
  const handleQuestionsComplete = useCallback((selections: IUserBets) => {
240
236
  setBets(selections);
241
- const pickers: Record<number, number> = {};
242
- for (const [mIdxStr, entry] of Object.entries(selections)) {
243
- pickers[Number(mIdxStr)] = entry.optionIdx;
244
- }
245
- setExpandedPicker(pickers);
246
237
  setUserFlowState("game");
247
238
  }, []);
248
239
 
@@ -252,10 +243,8 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
252
243
  if (existing?.optionIdx === oIdx) {
253
244
  const next = { ...prev };
254
245
  delete next[mIdx];
255
- setExpandedPicker((p) => { const n = { ...p }; delete n[mIdx]; return n; });
256
246
  return next;
257
247
  }
258
- setExpandedPicker((p) => ({ ...p, [mIdx]: oIdx }));
259
248
  return {
260
249
  ...prev,
261
250
  [mIdx]: { optionIdx: oIdx, amount: existing?.amount ?? 0, parlaySlot: existing?.parlaySlot ?? null },
@@ -358,7 +347,6 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
358
347
  config={config}
359
348
  bets={bets}
360
349
  onBetsChange={setBets}
361
- expandedPicker={expandedPicker}
362
350
  onOptionClick={handleOptionClick}
363
351
  onAmountSelect={handleAmountSelect}
364
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)",