aakashbansal commited on
Commit
e7635c0
·
1 Parent(s): e64fb74

same thing is happening. I see that the first message appears which has

Browse files
Files changed (1) hide show
  1. src/components/chatbot/chatbot.tsx +98 -71
src/components/chatbot/chatbot.tsx CHANGED
@@ -37,65 +37,74 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
37
  setMessages(prev => [...prev, { ...message, id: newId, timestamp: new Date() }]);
38
  }, []);
39
 
40
- const fetchNewMCQ = useCallback(async () => {
41
- if (!imageDataUri) {
42
- addMessage({ sender: 'ai', type: 'error', text: "Image data is not available to generate questions." });
43
- setIsLoading(false);
44
- return;
45
- }
46
- setIsLoading(true);
47
- // Important: Don't reset currentMCQ or isAwaitingAnswer here if this function is also used for "Next Question"
48
- // The initial reset is handled by the useEffect. For "Next Question", specific states are managed by handleOptionSelect.
49
-
50
- try {
51
- const mcqResult = await generateMCQ({ imageDataUri });
52
-
53
- let questionText = mcqResult.mcq;
54
- let finalMCQData = { ...mcqResult };
55
-
56
- if (isInitialQuestionRef.current) {
57
- questionText = `Okay, let's start with ${journeyTitle}! ${mcqResult.mcq}`;
58
- isInitialQuestionRef.current = false; // Set to false after using it for the first question
59
- }
60
- finalMCQData.mcq = questionText;
61
-
62
- setCurrentMCQ(finalMCQData);
63
- addMessage({ sender: 'ai', type: 'mcq', mcq: finalMCQData });
64
- setIsAwaitingAnswer(true); // Always true for a newly fetched question
65
- } catch (error) {
66
- console.error("Error generating MCQ:", error);
67
- const errorMessage = error instanceof Error ? error.message : "An unknown error occurred.";
68
- addMessage({ sender: 'ai', type: 'error', text: `Failed to generate question: ${errorMessage}` });
69
- toast({
70
- title: "Error",
71
- description: `Could not generate a new question. ${errorMessage}`,
72
- variant: "destructive",
73
- });
74
- } finally {
75
- setIsLoading(false);
76
- }
77
- // eslint-disable-next-line react-hooks/exhaustive-deps
78
- }, [imageDataUri, journeyTitle, addMessage]); // isInitialQuestionRef is a ref, not needed in deps if mutated correctly
79
 
80
  useEffect(() => {
81
- // Full reset for a new journey or when image data changes initially
 
 
82
  setMessages([]);
83
- setCurrentMCQ(null);
84
- setIsLoading(false);
85
  setIsAwaitingAnswer(false);
86
  setHasAnsweredCorrectly(false);
87
  setIncorrectAttempts([]);
88
- isInitialQuestionRef.current = true; // Reset flag for prepending intro
89
 
90
- if (imageDataUri) {
91
- fetchNewMCQ();
92
- } else {
93
- // If image data is not ready, show a placeholder.
94
- // The effect will re-run when imageDataUri becomes available because it's in the dependency array.
95
- addMessage({ sender: 'ai', type: 'text', text: "Preparing your journey..." });
96
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  // eslint-disable-next-line react-hooks/exhaustive-deps
98
- }, [imageDataUri, journeyTitle, fetchNewMCQ]); // fetchNewMCQ is memoized
 
99
 
100
  useEffect(() => {
101
  if (scrollAreaRef.current) {
@@ -115,7 +124,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
115
  });
116
 
117
  if (isCorrect) {
118
- setIsLoading(true); // Start loading before fetching explanation
119
  addMessage({ sender: 'ai', type: 'feedback', text: "That's correct! Well done.", isCorrect: true });
120
  try {
121
  const explanationResult = await explainCorrectAnswer({
@@ -131,11 +140,11 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
131
  }
132
  setHasAnsweredCorrectly(true);
133
  setIncorrectAttempts([]);
134
- setIsLoading(false); // Stop loading after explanation
135
  } else {
136
  const updatedIncorrectAttempts = [...incorrectAttempts, option];
137
  setIncorrectAttempts(updatedIncorrectAttempts);
138
- setIsLoading(true); // Start loading before fetching explanation
139
 
140
  if (updatedIncorrectAttempts.length >= currentMCQ.options.length - 1) {
141
  addMessage({
@@ -156,8 +165,8 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
156
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
157
  addMessage({ sender: 'ai', type: 'error', text: `Sorry, I couldn't provide an explanation for that. ${errorMsg}` });
158
  }
159
- setHasAnsweredCorrectly(true); // Mark as correctly handled
160
- setIncorrectAttempts([]); // Clear attempts for next question
161
  } else {
162
  try {
163
  const explanationResult = await explainIncorrectAnswer({
@@ -167,7 +176,6 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
167
  correctAnswer: currentMCQ.correctAnswer,
168
  });
169
  addMessage({ sender: 'ai', type: 'feedback', text: explanationResult.explanation, isCorrect: false });
170
- // Re-post the same MCQ for another attempt
171
  addMessage({ sender: 'ai', type: 'mcq', mcq: currentMCQ });
172
  } catch (error) {
173
  console.error("Error fetching explanation for incorrect answer:", error);
@@ -176,18 +184,41 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
176
  addMessage({ sender: 'ai', type: 'feedback', text: "That's not quite right. Try again!", isCorrect: false });
177
  addMessage({ sender: 'ai', type: 'mcq', mcq: currentMCQ });
178
  }
179
- setIsAwaitingAnswer(true); // Allow answering the re-posted MCQ
180
  }
181
- setIsLoading(false); // Stop loading after handling incorrect answer
182
  }
183
  };
184
 
185
- const handleNextQuestionClick = () => {
186
  setIsAwaitingAnswer(false);
187
  setHasAnsweredCorrectly(false);
188
- setCurrentMCQ(null); // Clear current MCQ before fetching new one
189
  setIncorrectAttempts([]);
190
- fetchNewMCQ();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  };
192
 
193
 
@@ -203,10 +234,9 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
203
  msg.type === 'mcq' &&
204
  msg.mcq &&
205
  currentMCQ &&
206
- msg.mcq.mcq === currentMCQ.mcq && // Compare by the question text itself
207
  JSON.stringify(msg.mcq.options) === JSON.stringify(currentMCQ.options);
208
 
209
- // Find the index of the last message in the array that is the current active MCQ
210
  const lastInstanceOfCurrentMCQIndex = messages.findLastIndex(
211
  (m) =>
212
  m.type === 'mcq' &&
@@ -216,11 +246,9 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
216
  JSON.stringify(m.mcq.options) === JSON.stringify(currentMCQ.options)
217
  );
218
 
219
- // Options should be interactive only for the last instance of the current MCQ,
220
- // and only if we are awaiting an answer and it hasn't been answered correctly yet.
221
  const shouldThisMessageBeInteractive =
222
  isThisMessageTheCurrentMCQ &&
223
- index === lastInstanceOfCurrentMCQIndex && // Ensure this is the LATEST instance
224
  isAwaitingAnswer &&
225
  !hasAnsweredCorrectly;
226
 
@@ -232,10 +260,9 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
232
  isAwaitingActiveMCQAnswer={shouldThisMessageBeInteractive}
233
  onOptionSelectActiveMCQ={handleOptionSelect}
234
  incorrectAttemptsForMCQ={
235
- // Pass incorrect attempts only to the latest, interactive instance
236
  shouldThisMessageBeInteractive
237
  ? incorrectAttempts
238
- : [] // Otherwise, no attempts should be highlighted for past/non-interactive MCQs
239
  }
240
  />
241
  );
@@ -269,7 +296,7 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
269
  {!imageDataUri && !isLoading && (
270
  <p className="text-center text-sm text-muted-foreground">Loading image, please wait...</p>
271
  )}
272
- {!currentMCQ && !isLoading && imageDataUri && messages.length === 0 && ( // Condition for when image is loaded but first question not yet.
273
  <p className="text-center text-sm text-muted-foreground">Loading first question...</p>
274
  )}
275
  {!currentMCQ && !isLoading && imageDataUri && messages.length > 0 && messages[messages.length -1]?.type === 'error' && (
@@ -279,4 +306,4 @@ export function Chatbot({ imageDataUri, journeyTitle }: ChatbotProps) {
279
  </div>
280
  );
281
  }
282
-
 
37
  setMessages(prev => [...prev, { ...message, id: newId, timestamp: new Date() }]);
38
  }, []);
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  useEffect(() => {
42
+ let ignore = false; // Flag to ignore results from stale effects
43
+
44
+ // Reset states for a new journey or when imageDataUri/journeyTitle changes
45
  setMessages([]);
46
+ setCurrentMCQ(null);
47
+ setIsLoading(false);
48
  setIsAwaitingAnswer(false);
49
  setHasAnsweredCorrectly(false);
50
  setIncorrectAttempts([]);
51
+ isInitialQuestionRef.current = true; // Reset for the new journey context
52
 
53
+ const performInitialFetch = async () => {
54
+ if (!imageDataUri) {
55
+ if (!ignore) {
56
+ addMessage({ sender: 'ai', type: 'text', text: "Preparing your journey..." });
57
+ }
58
+ return;
59
+ }
60
+
61
+ if (!ignore) setIsLoading(true);
62
+
63
+ try {
64
+ const mcqResult = await generateMCQ({ imageDataUri });
65
+
66
+ if (ignore) return;
67
+
68
+ let finalMCQData = { ...mcqResult };
69
+ let greetingWasPrepared = false;
70
+ if (isInitialQuestionRef.current) {
71
+ finalMCQData.mcq = `Okay, let's start with ${journeyTitle}! ${mcqResult.mcq}`;
72
+ greetingWasPrepared = true;
73
+ }
74
+
75
+ if (ignore) return;
76
+
77
+ if (greetingWasPrepared) {
78
+ isInitialQuestionRef.current = false;
79
+ }
80
+
81
+ setCurrentMCQ(finalMCQData);
82
+ addMessage({ sender: 'ai', type: 'mcq', mcq: finalMCQData });
83
+ setIsAwaitingAnswer(true);
84
+
85
+ } catch (error) {
86
+ if (ignore) return;
87
+ console.error("Error generating initial MCQ:", error);
88
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred.";
89
+ addMessage({ sender: 'ai', type: 'error', text: `Failed to generate initial question: ${errorMessage}` });
90
+ toast({
91
+ title: "Error",
92
+ description: `Could not generate the first question. ${errorMessage}`,
93
+ variant: "destructive",
94
+ });
95
+ } finally {
96
+ if (!ignore) setIsLoading(false);
97
+ }
98
+ };
99
+
100
+ performInitialFetch();
101
+
102
+ return () => {
103
+ ignore = true;
104
+ };
105
  // eslint-disable-next-line react-hooks/exhaustive-deps
106
+ }, [imageDataUri, journeyTitle, addMessage, toast]);
107
+
108
 
109
  useEffect(() => {
110
  if (scrollAreaRef.current) {
 
124
  });
125
 
126
  if (isCorrect) {
127
+ setIsLoading(true);
128
  addMessage({ sender: 'ai', type: 'feedback', text: "That's correct! Well done.", isCorrect: true });
129
  try {
130
  const explanationResult = await explainCorrectAnswer({
 
140
  }
141
  setHasAnsweredCorrectly(true);
142
  setIncorrectAttempts([]);
143
+ setIsLoading(false);
144
  } else {
145
  const updatedIncorrectAttempts = [...incorrectAttempts, option];
146
  setIncorrectAttempts(updatedIncorrectAttempts);
147
+ setIsLoading(true);
148
 
149
  if (updatedIncorrectAttempts.length >= currentMCQ.options.length - 1) {
150
  addMessage({
 
165
  const errorMsg = error instanceof Error ? error.message : "An unknown error occurred";
166
  addMessage({ sender: 'ai', type: 'error', text: `Sorry, I couldn't provide an explanation for that. ${errorMsg}` });
167
  }
168
+ setHasAnsweredCorrectly(true);
169
+ setIncorrectAttempts([]);
170
  } else {
171
  try {
172
  const explanationResult = await explainIncorrectAnswer({
 
176
  correctAnswer: currentMCQ.correctAnswer,
177
  });
178
  addMessage({ sender: 'ai', type: 'feedback', text: explanationResult.explanation, isCorrect: false });
 
179
  addMessage({ sender: 'ai', type: 'mcq', mcq: currentMCQ });
180
  } catch (error) {
181
  console.error("Error fetching explanation for incorrect answer:", error);
 
184
  addMessage({ sender: 'ai', type: 'feedback', text: "That's not quite right. Try again!", isCorrect: false });
185
  addMessage({ sender: 'ai', type: 'mcq', mcq: currentMCQ });
186
  }
187
+ setIsAwaitingAnswer(true);
188
  }
189
+ setIsLoading(false);
190
  }
191
  };
192
 
193
+ const handleNextQuestionClick = async () => {
194
  setIsAwaitingAnswer(false);
195
  setHasAnsweredCorrectly(false);
196
+ setCurrentMCQ(null);
197
  setIncorrectAttempts([]);
198
+
199
+ if (!imageDataUri) {
200
+ addMessage({ sender: 'ai', type: 'error', text: "Image data is not available to generate new questions." });
201
+ return;
202
+ }
203
+ setIsLoading(true);
204
+ try {
205
+ // isInitialQuestionRef should be false here, so no greeting prepended
206
+ const mcqResult = await generateMCQ({ imageDataUri });
207
+ setCurrentMCQ(mcqResult);
208
+ addMessage({ sender: 'ai', type: 'mcq', mcq: mcqResult });
209
+ setIsAwaitingAnswer(true);
210
+ } catch (error) {
211
+ console.error("Error generating next MCQ:", error);
212
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred.";
213
+ addMessage({ sender: 'ai', type: 'error', text: `Failed to generate new question: ${errorMessage}` });
214
+ toast({
215
+ title: "Error",
216
+ description: `Could not generate a new question. ${errorMessage}`,
217
+ variant: "destructive",
218
+ });
219
+ } finally {
220
+ setIsLoading(false);
221
+ }
222
  };
223
 
224
 
 
234
  msg.type === 'mcq' &&
235
  msg.mcq &&
236
  currentMCQ &&
237
+ msg.mcq.mcq === currentMCQ.mcq &&
238
  JSON.stringify(msg.mcq.options) === JSON.stringify(currentMCQ.options);
239
 
 
240
  const lastInstanceOfCurrentMCQIndex = messages.findLastIndex(
241
  (m) =>
242
  m.type === 'mcq' &&
 
246
  JSON.stringify(m.mcq.options) === JSON.stringify(currentMCQ.options)
247
  );
248
 
 
 
249
  const shouldThisMessageBeInteractive =
250
  isThisMessageTheCurrentMCQ &&
251
+ index === lastInstanceOfCurrentMCQIndex &&
252
  isAwaitingAnswer &&
253
  !hasAnsweredCorrectly;
254
 
 
260
  isAwaitingActiveMCQAnswer={shouldThisMessageBeInteractive}
261
  onOptionSelectActiveMCQ={handleOptionSelect}
262
  incorrectAttemptsForMCQ={
 
263
  shouldThisMessageBeInteractive
264
  ? incorrectAttempts
265
+ : []
266
  }
267
  />
268
  );
 
296
  {!imageDataUri && !isLoading && (
297
  <p className="text-center text-sm text-muted-foreground">Loading image, please wait...</p>
298
  )}
299
+ {!currentMCQ && !isLoading && imageDataUri && messages.length === 0 && (
300
  <p className="text-center text-sm text-muted-foreground">Loading first question...</p>
301
  )}
302
  {!currentMCQ && !isLoading && imageDataUri && messages.length > 0 && messages[messages.length -1]?.type === 'error' && (
 
306
  </div>
307
  );
308
  }
309
+