@devrongx/games 0.4.7 → 0.4.8

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.7",
3
+ "version": "0.4.8",
4
4
  "description": "Game UI components for sports prediction markets",
5
5
  "license": "MIT",
6
6
  "main": "./src/index.ts",
@@ -177,6 +177,30 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
177
177
  return () => { if (saveDraftTimerRef.current) clearTimeout(saveDraftTimerRef.current); };
178
178
  }, [bets, poolId, config]);
179
179
 
180
+ // Debounced save triggered from PreMatchQuestions on each option pick
181
+ const handleQuestionSelectionChange = useCallback((selections: IUserBets) => {
182
+ if (!poolId || !config) return;
183
+ if (saveDraftTimerRef.current) clearTimeout(saveDraftTimerRef.current);
184
+ saveDraftTimerRef.current = setTimeout(async () => {
185
+ try {
186
+ const draft = config.markets
187
+ .map((m, mIdx) => {
188
+ const b = selections[mIdx];
189
+ if (!b || !m.backendChallengeId) return null;
190
+ const option = m.options[b.optionIdx];
191
+ if (!option) return null;
192
+ return {
193
+ challenge_id: m.backendChallengeId,
194
+ selected_option: option.label.toLowerCase().replace(/\s+/g, "_"),
195
+ coin_amount: 0,
196
+ };
197
+ })
198
+ .filter((b): b is NonNullable<typeof b> => b !== null);
199
+ if (draft.length > 0) await saveTDDraftBetsApi(poolId, draft);
200
+ } catch { /* best-effort */ }
201
+ }, 1000);
202
+ }, [poolId, config]);
203
+
180
204
  const betSummary = useMemo(
181
205
  () => config ? calcBetSummary(bets, config) : { selectedCount: 0, compoundMultiplier: 0, totalEntry: 0, baseReward: 0, compoundedReward: 0, remainingBalance: 0, riskPercent: 0, potentialBalance: 0 },
182
206
  [bets, config],
@@ -328,7 +352,7 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
328
352
  )}
329
353
 
330
354
  {activeView === "questions" && (
331
- <PreMatchQuestions config={config} onComplete={handleQuestionsComplete} />
355
+ <PreMatchQuestions config={config} onComplete={handleQuestionsComplete} onSelectionChange={handleQuestionSelectionChange} />
332
356
  )}
333
357
 
334
358
  {activeView === "game" && (
@@ -11,6 +11,7 @@ import { useGamePopupStore } from "../../core/gamePopupStore";
11
11
  interface PreMatchQuestionsProps {
12
12
  config: IChallengeConfig;
13
13
  onComplete: (selections: IUserBets) => void;
14
+ onSelectionChange?: (selections: IUserBets) => void;
14
15
  }
15
16
 
16
17
  // ── Smooth typewriter — constant speed, clean and simple ──
@@ -263,7 +264,7 @@ const QuestionSlide = ({
263
264
  };
264
265
 
265
266
  // ── Main component ──
266
- export const PreMatchQuestions = ({ config, onComplete }: PreMatchQuestionsProps) => {
267
+ export const PreMatchQuestions = ({ config, onComplete, onSelectionChange }: PreMatchQuestionsProps) => {
267
268
  const goTo = useGamePopupStore(s => s.goTo);
268
269
 
269
270
  // -1 = start slide, 0..N-1 = question slides
@@ -283,19 +284,21 @@ export const PreMatchQuestions = ({ config, onComplete }: PreMatchQuestionsProps
283
284
  }, []);
284
285
 
285
286
  const handleOptionSelect = useCallback((mIdx: number, optionIdx: number) => {
287
+ const isDeselect = selections[mIdx]?.optionIdx === optionIdx;
288
+
286
289
  setSelections(prev => {
287
- // Deselect if same option tapped again
288
- if (prev[mIdx]?.optionIdx === optionIdx) {
290
+ if (isDeselect) {
289
291
  const next = { ...prev };
290
292
  delete next[mIdx];
293
+ onSelectionChange?.(next);
291
294
  return next;
292
295
  }
293
- // Only record the option pick no points spent yet. User sets bet amounts on game screen.
294
- return { ...prev, [mIdx]: { optionIdx, amount: 0, parlaySlot: null } };
296
+ const next = { ...prev, [mIdx]: { optionIdx, amount: 0, parlaySlot: null } };
297
+ onSelectionChange?.(next);
298
+ return next;
295
299
  });
296
300
 
297
301
  // Auto-advance after a brief pause (only if selecting, not deselecting)
298
- const isDeselect = selections[mIdx]?.optionIdx === optionIdx;
299
302
  if (!isDeselect) {
300
303
  setTimeout(() => {
301
304
  if (mIdx < totalQuestions - 1) {
@@ -311,7 +314,7 @@ export const PreMatchQuestions = ({ config, onComplete }: PreMatchQuestionsProps
311
314
  }
312
315
  }, 500);
313
316
  }
314
- }, [config.markets, selections, totalQuestions, onComplete, goTo]);
317
+ }, [config.markets, selections, totalQuestions, onComplete, onSelectionChange, goTo]);
315
318
 
316
319
  const seekNext = useCallback(() => {
317
320
  if (step === -1) {