@devrongx/games 0.4.7 → 0.4.9
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
|
@@ -125,6 +125,8 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
|
|
|
125
125
|
|
|
126
126
|
// ── Draft restore: when entry + config load, seed bets from draft_selections ─
|
|
127
127
|
const draftRestoredRef = useRef(false);
|
|
128
|
+
const [restoredSelections, setRestoredSelections] = useState<IUserBets | undefined>(undefined);
|
|
129
|
+
const [restoredStep, setRestoredStep] = useState<number | undefined>(undefined);
|
|
128
130
|
useEffect(() => {
|
|
129
131
|
if (!poolId || !config || !entryData || draftRestoredRef.current) return;
|
|
130
132
|
const draft = entryData.entry.draft_selections;
|
|
@@ -140,9 +142,18 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
|
|
|
140
142
|
if (optionIdx < 0) continue;
|
|
141
143
|
restored[mIdx] = { optionIdx, amount: d.coin_amount, parlaySlot: null };
|
|
142
144
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
const answeredCount = Object.keys(restored).length;
|
|
146
|
+
if (answeredCount === 0) return;
|
|
147
|
+
const allAnswered = answeredCount === config.markets.length;
|
|
148
|
+
setBets(restored);
|
|
149
|
+
if (allAnswered) {
|
|
150
|
+
// All questions done — go straight to game screen
|
|
145
151
|
setUserFlowState("game");
|
|
152
|
+
} else {
|
|
153
|
+
// Partial — restore into questions at the first unanswered question
|
|
154
|
+
setRestoredSelections(restored);
|
|
155
|
+
setRestoredStep(answeredCount); // first unanswered index
|
|
156
|
+
setUserFlowState("questions");
|
|
146
157
|
}
|
|
147
158
|
draftRestoredRef.current = true;
|
|
148
159
|
}, [poolId, config, entryData]);
|
|
@@ -177,6 +188,30 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
|
|
|
177
188
|
return () => { if (saveDraftTimerRef.current) clearTimeout(saveDraftTimerRef.current); };
|
|
178
189
|
}, [bets, poolId, config]);
|
|
179
190
|
|
|
191
|
+
// Debounced save triggered from PreMatchQuestions on each option pick
|
|
192
|
+
const handleQuestionSelectionChange = useCallback((selections: IUserBets) => {
|
|
193
|
+
if (!poolId || !config) return;
|
|
194
|
+
if (saveDraftTimerRef.current) clearTimeout(saveDraftTimerRef.current);
|
|
195
|
+
saveDraftTimerRef.current = setTimeout(async () => {
|
|
196
|
+
try {
|
|
197
|
+
const draft = config.markets
|
|
198
|
+
.map((m, mIdx) => {
|
|
199
|
+
const b = selections[mIdx];
|
|
200
|
+
if (!b || !m.backendChallengeId) return null;
|
|
201
|
+
const option = m.options[b.optionIdx];
|
|
202
|
+
if (!option) return null;
|
|
203
|
+
return {
|
|
204
|
+
challenge_id: m.backendChallengeId,
|
|
205
|
+
selected_option: option.label.toLowerCase().replace(/\s+/g, "_"),
|
|
206
|
+
coin_amount: 0,
|
|
207
|
+
};
|
|
208
|
+
})
|
|
209
|
+
.filter((b): b is NonNullable<typeof b> => b !== null);
|
|
210
|
+
if (draft.length > 0) await saveTDDraftBetsApi(poolId, draft);
|
|
211
|
+
} catch { /* best-effort */ }
|
|
212
|
+
}, 1000);
|
|
213
|
+
}, [poolId, config]);
|
|
214
|
+
|
|
180
215
|
const betSummary = useMemo(
|
|
181
216
|
() => config ? calcBetSummary(bets, config) : { selectedCount: 0, compoundMultiplier: 0, totalEntry: 0, baseReward: 0, compoundedReward: 0, remainingBalance: 0, riskPercent: 0, potentialBalance: 0 },
|
|
182
217
|
[bets, config],
|
|
@@ -328,7 +363,7 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
|
|
|
328
363
|
)}
|
|
329
364
|
|
|
330
365
|
{activeView === "questions" && (
|
|
331
|
-
<PreMatchQuestions config={config} onComplete={handleQuestionsComplete} />
|
|
366
|
+
<PreMatchQuestions config={config} onComplete={handleQuestionsComplete} onSelectionChange={handleQuestionSelectionChange} initialSelections={restoredSelections} initialStep={restoredStep} />
|
|
332
367
|
)}
|
|
333
368
|
|
|
334
369
|
{activeView === "game" && (
|
|
@@ -11,6 +11,9 @@ import { useGamePopupStore } from "../../core/gamePopupStore";
|
|
|
11
11
|
interface PreMatchQuestionsProps {
|
|
12
12
|
config: IChallengeConfig;
|
|
13
13
|
onComplete: (selections: IUserBets) => void;
|
|
14
|
+
onSelectionChange?: (selections: IUserBets) => void;
|
|
15
|
+
initialSelections?: IUserBets;
|
|
16
|
+
initialStep?: number; // question index (0-based) to start at; -1 = start slide
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
// ── Smooth typewriter — constant speed, clean and simple ──
|
|
@@ -263,13 +266,13 @@ const QuestionSlide = ({
|
|
|
263
266
|
};
|
|
264
267
|
|
|
265
268
|
// ── Main component ──
|
|
266
|
-
export const PreMatchQuestions = ({ config, onComplete }: PreMatchQuestionsProps) => {
|
|
269
|
+
export const PreMatchQuestions = ({ config, onComplete, onSelectionChange, initialSelections, initialStep }: PreMatchQuestionsProps) => {
|
|
267
270
|
const goTo = useGamePopupStore(s => s.goTo);
|
|
268
271
|
|
|
269
272
|
// -1 = start slide, 0..N-1 = question slides
|
|
270
|
-
const [step, setStep] = useState(-1);
|
|
273
|
+
const [step, setStep] = useState<number>(initialStep ?? -1);
|
|
271
274
|
const [direction, setDirection] = useState(1);
|
|
272
|
-
const [selections, setSelections] = useState<IUserBets>({});
|
|
275
|
+
const [selections, setSelections] = useState<IUserBets>(initialSelections ?? {});
|
|
273
276
|
|
|
274
277
|
const totalQuestions = config.markets.length;
|
|
275
278
|
// Total steps for progress bar: start + questions
|
|
@@ -283,19 +286,21 @@ export const PreMatchQuestions = ({ config, onComplete }: PreMatchQuestionsProps
|
|
|
283
286
|
}, []);
|
|
284
287
|
|
|
285
288
|
const handleOptionSelect = useCallback((mIdx: number, optionIdx: number) => {
|
|
289
|
+
const isDeselect = selections[mIdx]?.optionIdx === optionIdx;
|
|
290
|
+
|
|
286
291
|
setSelections(prev => {
|
|
287
|
-
|
|
288
|
-
if (prev[mIdx]?.optionIdx === optionIdx) {
|
|
292
|
+
if (isDeselect) {
|
|
289
293
|
const next = { ...prev };
|
|
290
294
|
delete next[mIdx];
|
|
295
|
+
onSelectionChange?.(next);
|
|
291
296
|
return next;
|
|
292
297
|
}
|
|
293
|
-
|
|
294
|
-
|
|
298
|
+
const next = { ...prev, [mIdx]: { optionIdx, amount: 0, parlaySlot: null } };
|
|
299
|
+
onSelectionChange?.(next);
|
|
300
|
+
return next;
|
|
295
301
|
});
|
|
296
302
|
|
|
297
303
|
// Auto-advance after a brief pause (only if selecting, not deselecting)
|
|
298
|
-
const isDeselect = selections[mIdx]?.optionIdx === optionIdx;
|
|
299
304
|
if (!isDeselect) {
|
|
300
305
|
setTimeout(() => {
|
|
301
306
|
if (mIdx < totalQuestions - 1) {
|
|
@@ -311,7 +316,7 @@ export const PreMatchQuestions = ({ config, onComplete }: PreMatchQuestionsProps
|
|
|
311
316
|
}
|
|
312
317
|
}, 500);
|
|
313
318
|
}
|
|
314
|
-
}, [config.markets, selections, totalQuestions, onComplete, goTo]);
|
|
319
|
+
}, [config.markets, selections, totalQuestions, onComplete, onSelectionChange, goTo]);
|
|
315
320
|
|
|
316
321
|
const seekNext = useCallback(() => {
|
|
317
322
|
if (step === -1) {
|