Skip to content

Commit e91e732

Browse files
committed
Fix quiz duplicate detection for initial state
- Add currentQuizId check in loadNextQuiz to prevent loading same quiz - Use currentQuizId as fallback for excludeQuizId when lastShownId is null - This fixes the issue where first quiz load would show same quiz again The problem was that on initial load, lastShownId was null but there was already a prefetched quiz available. Now we check both lastShownId and currentQuizId to prevent duplicates.
1 parent 4c6a466 commit e91e732

File tree

3 files changed

+88
-19
lines changed

3 files changed

+88
-19
lines changed

app/repo/quizRepo.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,12 @@ export const getCachedExerciseToAttempt = (
152152
ORDER BY
153153
q.created_at DESC
154154
LIMIT 1`;
155-
155+
156156
if (excludeQuizId) {
157157
stmt = db.prepare<[number, string, string, string, number]>(sql);
158-
row = stmt.get(userId, passageLanguage, questionLanguage, level, excludeQuizId) as QuizRow | undefined;
158+
row = stmt.get(userId, passageLanguage, questionLanguage, level, excludeQuizId) as
159+
| QuizRow
160+
| undefined;
159161
} else {
160162
stmt = db.prepare<[number, string, string, string]>(sql);
161163
row = stmt.get(userId, passageLanguage, questionLanguage, level) as QuizRow | undefined;
@@ -169,10 +171,12 @@ export const getCachedExerciseToAttempt = (
169171
${excludeIdClause}
170172
ORDER BY created_at DESC
171173
LIMIT 1`;
172-
174+
173175
if (excludeQuizId) {
174176
stmt = db.prepare<[string, string, string, number]>(sql);
175-
row = stmt.get(passageLanguage, questionLanguage, level, excludeQuizId) as QuizRow | undefined;
177+
row = stmt.get(passageLanguage, questionLanguage, level, excludeQuizId) as
178+
| QuizRow
179+
| undefined;
176180
} else {
177181
stmt = db.prepare<[string, string, string]>(sql);
178182
row = stmt.get(passageLanguage, questionLanguage, level) as QuizRow | undefined;

app/store/quizSlice.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export const createQuizSlice: StateCreator<
156156
},
157157
resetQuizWithNewData: (newQuizData: PartialQuizData, quizId: number) => {
158158
console.log('[Quiz] Loading quiz ID:', quizId, 'Previous quiz ID:', get().lastShownQuizId);
159-
159+
160160
get().stopPassageSpeech();
161161
get().resetQuizState();
162162
set((state) => {
@@ -172,21 +172,33 @@ export const createQuizSlice: StateCreator<
172172
get().setShowQuestionSection(true);
173173
get().setShowExplanation(false);
174174
get().setShowContent(true);
175-
175+
176176
// Prefetch the next quiz in the background for smooth transitions
177177
console.log('[Quiz] Starting prefetch for next quiz...');
178178
void get().generateText(true);
179179
},
180180
loadNextQuiz: () => {
181181
const nextQuiz = get().nextQuizAvailable;
182182
const lastShownId = get().lastShownQuizId;
183-
184-
console.log('[Quiz] loadNextQuiz called. Next quiz available:', !!nextQuiz, 'Next quiz ID:', nextQuiz?.quizId, 'Last shown ID:', lastShownId);
185-
183+
const currentQuizId = get().currentQuizId;
184+
185+
console.log(
186+
'[Quiz] loadNextQuiz called. Next quiz available:',
187+
!!nextQuiz,
188+
'Next quiz ID:',
189+
nextQuiz?.quizId,
190+
'Last shown ID:',
191+
lastShownId,
192+
'Current quiz ID:',
193+
currentQuizId
194+
);
195+
186196
if (nextQuiz) {
187-
// Check if the prefetched quiz is the same as the one we just showed
188-
if (nextQuiz.quizId === lastShownId) {
189-
console.warn('[Quiz] Prefetched quiz is the same as last shown! Clearing it and generating new quiz.');
197+
// Check if the prefetched quiz is the same as the one we just showed OR currently showing
198+
if (nextQuiz.quizId === lastShownId || nextQuiz.quizId === currentQuizId) {
199+
console.warn(
200+
'[Quiz] Prefetched quiz is the same as last shown or current! Clearing it and generating new quiz.'
201+
);
190202
// Clear the duplicate prefetched quiz to prevent infinite loop
191203
set({ nextQuizAvailable: null });
192204
void get().generateText(); // Generate a fresh quiz instead
@@ -246,7 +258,7 @@ export const createQuizSlice: StateCreator<
246258
generateText: async (isPrefetch = false): Promise<void> => {
247259
const lastShownId = get().lastShownQuizId;
248260
console.log('[Quiz] generateText called. isPrefetch:', isPrefetch, 'lastShownId:', lastShownId);
249-
261+
250262
if (!isPrefetch && get().nextQuizAvailable) {
251263
console.log('[Quiz] Using loadNextQuiz flow (nextQuizAvailable exists)');
252264
get().loadNextQuiz();
@@ -261,12 +273,16 @@ export const createQuizSlice: StateCreator<
261273
get().setShowExplanation(false);
262274
}
263275
try {
264-
console.log('[Quiz] Calling generateExerciseResponse...', 'excludeQuizId:', lastShownId);
276+
console.log(
277+
'[Quiz] Calling generateExerciseResponse...',
278+
'excludeQuizId:',
279+
lastShownId || get().currentQuizId
280+
);
265281
const response = await generateExerciseResponse({
266282
passageLanguage: get().passageLanguage,
267283
questionLanguage: get().generatedQuestionLanguage,
268284
cefrLevel: get().cefrLevel,
269-
excludeQuizId: lastShownId,
285+
excludeQuizId: lastShownId || get().currentQuizId,
270286
});
271287
if ('error' in response && response.error) {
272288
if (!isPrefetch) {
@@ -293,16 +309,16 @@ export const createQuizSlice: StateCreator<
293309
const validatedResponse = parseResult.data;
294310
const quizData: PartialQuizData = validatedResponse.quizData;
295311
const newQuizId = validatedResponse.quizId;
296-
312+
297313
if (!quizData.language) quizData.language = get().passageLanguage;
298-
314+
299315
console.log('[Quiz] Generated quiz ID:', newQuizId, 'isPrefetch:', isPrefetch);
300-
316+
301317
// Check if we accidentally got the same quiz as last shown
302318
if (newQuizId === lastShownId) {
303319
console.warn('[Quiz] WARNING: Generated quiz has same ID as last shown quiz!', newQuizId);
304320
}
305-
321+
306322
if (isPrefetch) {
307323
console.log('[Quiz] Storing prefetched quiz:', newQuizId);
308324
set({

test/e2e/debug-quiz.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Quiz Flow Debug', () => {
4+
test('debug quiz generation and console logs', async ({ page }) => {
5+
// Listen to console logs
6+
const consoleLogs: string[] = [];
7+
page.on('console', (msg) => {
8+
if (msg.text().includes('[Quiz]')) {
9+
consoleLogs.push(msg.text());
10+
}
11+
});
12+
13+
await page.goto('/en');
14+
15+
// Load first quiz
16+
const generateButton = page.locator('[data-testid="generate-button"]');
17+
await generateButton.click();
18+
19+
const passageLocator = page.locator('[data-testid="passage-text"]');
20+
await expect(passageLocator).toBeVisible({ timeout: 10000 });
21+
22+
const firstPassage = await passageLocator.textContent();
23+
console.log('First quiz:', firstPassage);
24+
25+
// Answer and get next quiz
26+
const firstOption = page.locator('[data-testid="answer-option-0"]');
27+
await firstOption.click();
28+
29+
const feedbackLocator = page.locator('[data-testid="feedback-explanation"]');
30+
await expect(feedbackLocator).toBeVisible({ timeout: 5000 });
31+
32+
await generateButton.click();
33+
await expect(passageLocator).toBeVisible({ timeout: 10000 });
34+
35+
const secondPassage = await passageLocator.textContent();
36+
console.log('Second quiz:', secondPassage);
37+
38+
// Print console logs
39+
console.log('\n=== Console Logs ===');
40+
consoleLogs.forEach((log) => console.log(log));
41+
42+
// Check if they're different
43+
if (firstPassage === secondPassage) {
44+
console.log('❌ SAME QUIZ DETECTED!');
45+
} else {
46+
console.log('✅ Different quizzes loaded');
47+
}
48+
});
49+
});

0 commit comments

Comments
 (0)