@devrongx/games 0.4.8 → 0.4.10

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.8",
3
+ "version": "0.4.10",
4
4
  "description": "Game UI components for sports prediction markets",
5
5
  "license": "MIT",
6
6
  "main": "./src/index.ts",
@@ -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,42 +142,23 @@ 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
- if (Object.keys(restored).length > 0) {
144
- setBets(restored);
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]);
149
160
 
150
- // ── Debounced draft save: persists bets to backend 1s after last change ───
151
161
  const saveDraftTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
152
- useEffect(() => {
153
- if (!poolId || Object.keys(bets).length === 0 || !config) return;
154
- if (saveDraftTimerRef.current) clearTimeout(saveDraftTimerRef.current);
155
- saveDraftTimerRef.current = setTimeout(async () => {
156
- try {
157
- const selections = config.markets
158
- .map((m, mIdx) => {
159
- const b = bets[mIdx];
160
- if (!b || !m.backendChallengeId) return null;
161
- const option = m.options[b.optionIdx];
162
- if (!option) return null;
163
- return {
164
- challenge_id: m.backendChallengeId,
165
- selected_option: option.label.toLowerCase().replace(/\s+/g, "_"),
166
- coin_amount: b.amount,
167
- };
168
- })
169
- .filter((b): b is NonNullable<typeof b> => b !== null);
170
- if (selections.length > 0) {
171
- await saveTDDraftBetsApi(poolId, selections);
172
- }
173
- } catch {
174
- // Silent — draft save is best-effort
175
- }
176
- }, 1000);
177
- return () => { if (saveDraftTimerRef.current) clearTimeout(saveDraftTimerRef.current); };
178
- }, [bets, poolId, config]);
179
162
 
180
163
  // Debounced save triggered from PreMatchQuestions on each option pick
181
164
  const handleQuestionSelectionChange = useCallback((selections: IUserBets) => {
@@ -352,7 +335,7 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
352
335
  )}
353
336
 
354
337
  {activeView === "questions" && (
355
- <PreMatchQuestions config={config} onComplete={handleQuestionsComplete} onSelectionChange={handleQuestionSelectionChange} />
338
+ <PreMatchQuestions config={config} onComplete={handleQuestionsComplete} onSelectionChange={handleQuestionSelectionChange} initialSelections={restoredSelections} initialStep={restoredStep} />
356
339
  )}
357
340
 
358
341
  {activeView === "game" && (
@@ -12,6 +12,8 @@ interface PreMatchQuestionsProps {
12
12
  config: IChallengeConfig;
13
13
  onComplete: (selections: IUserBets) => void;
14
14
  onSelectionChange?: (selections: IUserBets) => void;
15
+ initialSelections?: IUserBets;
16
+ initialStep?: number; // question index (0-based) to start at; -1 = start slide
15
17
  }
16
18
 
17
19
  // ── Smooth typewriter — constant speed, clean and simple ──
@@ -264,13 +266,13 @@ const QuestionSlide = ({
264
266
  };
265
267
 
266
268
  // ── Main component ──
267
- export const PreMatchQuestions = ({ config, onComplete, onSelectionChange }: PreMatchQuestionsProps) => {
269
+ export const PreMatchQuestions = ({ config, onComplete, onSelectionChange, initialSelections, initialStep }: PreMatchQuestionsProps) => {
268
270
  const goTo = useGamePopupStore(s => s.goTo);
269
271
 
270
272
  // -1 = start slide, 0..N-1 = question slides
271
- const [step, setStep] = useState(-1);
273
+ const [step, setStep] = useState<number>(initialStep ?? -1);
272
274
  const [direction, setDirection] = useState(1);
273
- const [selections, setSelections] = useState<IUserBets>({});
275
+ const [selections, setSelections] = useState<IUserBets>(initialSelections ?? {});
274
276
 
275
277
  const totalQuestions = config.markets.length;
276
278
  // Total steps for progress bar: start + questions