Skip to content

Commit d38fef4

Browse files
committed
fix explanation bug
1 parent 7f18920 commit d38fef4

1 file changed

Lines changed: 119 additions & 89 deletions

File tree

src/views/TopicHistogram.tsx

Lines changed: 119 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,17 @@ const TopicHistogram: FC = () => {
272272

273273
// State for API key
274274
const [apiKey, setApiKey] = useState<string>('');
275+
// Add state to track if we need to fetch explanation after API key is set
276+
const [pendingExplanationTopic, setPendingExplanationTopic] = useState<string | null>(null);
277+
278+
// Add cleanup effect for API key
279+
useEffect(() => {
280+
// Clear API key and pending explanation when component unmounts
281+
return () => {
282+
setApiKey('');
283+
setPendingExplanationTopic(null);
284+
};
285+
}, []);
275286

276287
// Add API key input modal - initialize to false instead of !apiKey
277288
const [showApiKeyModal, setShowApiKeyModal] = useState(false);
@@ -281,109 +292,68 @@ const TopicHistogram: FC = () => {
281292

282293
// Function to handle topic click
283294
const handleTopicClick = (topic: string) => {
284-
console.log('Topic clicked:', topic);
285-
console.log('Current API key:', apiKey ? 'Present' : 'Missing');
286-
287295
if (!apiKey) {
288-
console.log('No API key, showing modal');
289-
// Show API key modal if no key is set
296+
// Store the topic that was clicked for later explanation
297+
setPendingExplanationTopic(topic);
290298
setShowApiKeyModal(true);
291299
return;
292300
}
293301

294-
console.log('Starting explanation fetch for topic:', topic);
295-
// Show explanation modal immediately with loading state
302+
// If we have an API key, fetch explanation immediately
296303
setSelectedTopicForExplanation(topic);
297304
setTopicExplanation(""); // Clear previous explanation
298305
setIsLoadingExplanation(true); // Set loading state
299-
// Then fetch the explanation
300306
fetchTopicExplanation(topic);
301307
};
302308

303309
// Function to save API key and fetch explanation if a topic was clicked
304310
const saveApiKey = (key: string) => {
305-
setApiKey(key);
311+
// Trim the key to remove any accidental whitespace
312+
const trimmedKey = key.trim();
313+
setApiKey(trimmedKey);
306314
setShowApiKeyModal(false);
307315

308-
// If there was a topic waiting for explanation, fetch it now
309-
if (selectedTopicForExplanation) {
310-
fetchTopicExplanation(selectedTopicForExplanation);
316+
// If there was a pending topic waiting for explanation, fetch it now
317+
if (pendingExplanationTopic) {
318+
setSelectedTopicForExplanation(pendingExplanationTopic);
319+
setTopicExplanation(""); // Clear previous explanation
320+
setIsLoadingExplanation(true); // Set loading state
321+
fetchTopicExplanation(pendingExplanationTopic);
322+
setPendingExplanationTopic(null); // Clear the pending topic
311323
}
312324
};
313325

314-
// Effect to check if search term and topic are provided
315-
useEffect(() => {
316-
if (!searchTerm) {
317-
notify({
318-
message: "No search term provided. Please enter a topic to search.",
319-
type: "warning"
320-
});
321-
navigate('/');
322-
} else {
323-
setOriginalTopic(userTopic);
324-
}
325-
}, [searchTerm, userTopic, navigate, notify]);
326-
327-
// Effect to handle topic extraction when search term changes
328-
useEffect(() => {
329-
// Add a check for extractedTopics length to prevent refetching
330-
if (!userTopic || extractedTopics.length > 0) return;
331-
332-
setIsLoading(true);
326+
// Function to clear API key
327+
const clearApiKey = () => {
328+
setApiKey('');
329+
setShowApiKeyModal(false);
330+
setSelectedTopicForExplanation(null);
331+
setPendingExplanationTopic(null); // Also clear any pending explanation
332+
};
333333

334-
fetch(API_ENDPOINTS.PROCESS_TOPICS, {
335-
method: 'POST',
336-
headers: {
337-
'Content-Type': 'application/json',
338-
},
339-
body: JSON.stringify({
340-
topic: userTopic,
341-
searchTerm: searchTerm
342-
})
343-
})
344-
.then(response => {
345-
if (!response.ok) {
346-
throw new Error(`HTTP error! status: ${response.status}`);
347-
}
348-
return response.json();
349-
})
350-
.then(data => {
351-
if (data.success && data.data.length === 0) {
352-
notify({
353-
message: "No topics found for this search. Try a different topic.",
354-
type: "warning"
355-
});
356-
}
357-
setExtractedTopics(data.data || []);
358-
})
359-
.catch(error => {
360-
console.error('Fetch error:', error);
361-
notify({
362-
message: "Failed to fetch topics. Please try again.",
363-
type: "error"
364-
});
365-
setExtractedTopics([]);
366-
})
367-
.finally(() => {
368-
setIsLoading(false);
369-
});
370-
}, [userTopic]);
334+
// Function to handle API key input change
335+
const handleApiKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
336+
// Clear any existing key when user starts typing
337+
if (apiKey) {
338+
setApiKey('');
339+
setPendingExplanationTopic(null); // Clear pending explanation when key is cleared
340+
}
341+
setApiKey(e.target.value);
342+
};
371343

372344
// Function to fetch topic explanation
373345
const fetchTopicExplanation = async (topic: string) => {
374-
console.log('Fetching explanation for topic:', topic);
375346
if (!apiKey) {
376-
console.log('No API key in fetchTopicExplanation');
377347
notify({
378348
message: "Please set your Google API key first",
379349
type: "warning"
380350
});
381-
setSelectedTopicForExplanation(null); // Close modal if no API key
351+
setSelectedTopicForExplanation(null);
352+
setPendingExplanationTopic(topic); // Store the topic for later
382353
return;
383354
}
384355

385356
try {
386-
console.log('Making API request to:', API_ENDPOINTS.EXPLAIN_TOPIC);
387357
const response = await fetch(API_ENDPOINTS.EXPLAIN_TOPIC, {
388358
method: 'POST',
389359
headers: {
@@ -397,15 +367,14 @@ const TopicHistogram: FC = () => {
397367
})
398368
});
399369

400-
console.log('Response status:', response.status);
401370
if (!response.ok) {
402371
throw new Error(`HTTP error! status: ${response.status}`);
403372
}
404373

405374
const data = await response.json();
406-
console.log('Response data:', data);
407375
if (data.success) {
408-
setTopicExplanation(data.explanation);
376+
const explanation = typeof data.explanation === 'object' ? data.explanation.explanation : data.explanation;
377+
setTopicExplanation(explanation || "No explanation available.");
409378
} else {
410379
throw new Error(data.message || 'Failed to get explanation');
411380
}
@@ -416,6 +385,7 @@ const TopicHistogram: FC = () => {
416385
type: "error"
417386
});
418387
setTopicExplanation("Sorry, I couldn't generate an explanation for this topic at the moment.");
388+
// Don't clear the selected topic on error, so user can try again
419389
} finally {
420390
setIsLoadingExplanation(false);
421391
}
@@ -431,7 +401,7 @@ const TopicHistogram: FC = () => {
431401
const handleRequestSuggestions = async (model: string, prompt: string, apiKey: string, topics: string[]) => {
432402
setLlmSuggestionsState([]); // Clear previous suggestions immediately
433403
try {
434-
console.log('Requesting AI suggestions with:', { model, prompt, apiKey, topics });
404+
// console.log('Requesting AI suggestions with:', { model, prompt, apiKey, topics });
435405
const response = await fetch(API_ENDPOINTS.AI_PROCESS, {
436406
method: 'POST',
437407
headers: {
@@ -451,7 +421,7 @@ const TopicHistogram: FC = () => {
451421
}
452422

453423
const data = await response.json();
454-
console.log('Received AI suggestions:', data);
424+
// console.log('Received AI suggestions:', data);
455425

456426
if (data.success && Array.isArray(data.result)) {
457427
// Update state synchronously
@@ -488,7 +458,7 @@ const TopicHistogram: FC = () => {
488458
}, 100);
489459
});
490460

491-
console.log('State updated with suggestions:', data.result);
461+
// console.log('State updated with suggestions:', data.result);
492462
} else {
493463
throw new Error('Invalid response format from AI service');
494464
}
@@ -508,6 +478,64 @@ const TopicHistogram: FC = () => {
508478
window.location.href = window.location.origin;
509479
};
510480

481+
// Effect to handle topic extraction when search term changes
482+
useEffect(() => {
483+
// Add a check for extractedTopics length to prevent refetching
484+
if (!userTopic || extractedTopics.length > 0) return;
485+
486+
setIsLoading(true);
487+
488+
fetch(API_ENDPOINTS.PROCESS_TOPICS, {
489+
method: 'POST',
490+
headers: {
491+
'Content-Type': 'application/json',
492+
},
493+
body: JSON.stringify({
494+
topic: userTopic,
495+
searchTerm: searchTerm
496+
})
497+
})
498+
.then(response => {
499+
if (!response.ok) {
500+
throw new Error(`HTTP error! status: ${response.status}`);
501+
}
502+
return response.json();
503+
})
504+
.then(data => {
505+
if (data.success && data.data.length === 0) {
506+
notify({
507+
message: "No topics found for this search. Try a different topic.",
508+
type: "warning"
509+
});
510+
}
511+
setExtractedTopics(data.data || []);
512+
})
513+
.catch(error => {
514+
console.error('Fetch error:', error);
515+
notify({
516+
message: "Failed to fetch topics. Please try again.",
517+
type: "error"
518+
});
519+
setExtractedTopics([]);
520+
})
521+
.finally(() => {
522+
setIsLoading(false);
523+
});
524+
}, [userTopic]);
525+
526+
// Effect to check if search term and topic are provided
527+
useEffect(() => {
528+
if (!searchTerm) {
529+
notify({
530+
message: "No search term provided. Please enter a topic to search.",
531+
type: "warning"
532+
});
533+
navigate('/');
534+
} else {
535+
setOriginalTopic(userTopic);
536+
}
537+
}, [searchTerm, userTopic, navigate, notify]);
538+
511539
return (
512540
<main className="container-fluid py-4" style={{ height: '100vh', overflowY: 'auto' }}>
513541
{/* API Key Modal */}
@@ -520,10 +548,7 @@ const TopicHistogram: FC = () => {
520548
<button
521549
type="button"
522550
className="btn-close"
523-
onClick={() => {
524-
setShowApiKeyModal(false);
525-
setSelectedTopicForExplanation(null);
526-
}}
551+
onClick={clearApiKey}
527552
aria-label="Close"
528553
/>
529554
</div>
@@ -535,11 +560,19 @@ const TopicHistogram: FC = () => {
535560
className="form-control"
536561
id="apiKey"
537562
value={apiKey}
538-
onChange={(e) => setApiKey(e.target.value)}
563+
onChange={handleApiKeyChange}
539564
placeholder="Enter your Google Gemini API key"
565+
autoComplete="off"
540566
/>
541567
<div className="form-text">
542-
Your Google Gemini API key is required to get topic explanations. It is stored locally in your browser and is never shared with our servers.
568+
Your Google Gemini API key is required to get topic explanations.
569+
<strong>Important security notes:</strong>
570+
<ul className="mt-2 mb-0">
571+
<li>The key is stored only in memory and is cleared when you close the page</li>
572+
<li>It is never saved to disk or sent to our servers</li>
573+
<li>It is only used to make direct API calls to Google's services</li>
574+
<li>Please use a key with appropriate restrictions set in Google Cloud Console</li>
575+
</ul>
543576
<br />
544577
<a href="https://ai.google.dev/" target="_blank" rel="noopener noreferrer">
545578
Get your API key from Google AI Studio
@@ -551,18 +584,15 @@ const TopicHistogram: FC = () => {
551584
<button
552585
type="button"
553586
className="btn btn-secondary"
554-
onClick={() => {
555-
setShowApiKeyModal(false);
556-
setSelectedTopicForExplanation(null);
557-
}}
587+
onClick={clearApiKey}
558588
>
559589
Cancel
560590
</button>
561591
<button
562592
type="button"
563593
className="btn btn-primary"
564594
onClick={() => saveApiKey(apiKey)}
565-
disabled={!apiKey}
595+
disabled={!apiKey.trim()}
566596
>
567597
Save & Continue
568598
</button>

0 commit comments

Comments
 (0)