Skip to content

Commit 0c73cbc

Browse files
committed
fix(lint): resolve react-hooks lint errors after eslint ecosystem upgrade
1 parent 11ba20a commit 0c73cbc

15 files changed

Lines changed: 769 additions & 784 deletions

File tree

SortVision/src/components/StarOnGithubPopup.jsx

Lines changed: 17 additions & 432 deletions
Large diffs are not rendered by default.

SortVision/src/components/chatbot/ChatAssistant.jsx

Lines changed: 18 additions & 326 deletions
Original file line numberDiff line numberDiff line change
@@ -1,339 +1,31 @@
1-
import React, { useState, useEffect, useRef, useCallback } from 'react';
2-
import { useAlgorithmState } from '@/context/AlgorithmState';
3-
import { useLanguage } from '@/context/LanguageContext';
4-
import { useAudio } from '@/hooks/useAudio';
1+
import React from 'react';
52
import { useMobileOverlay } from '@/components/MobileOverlay';
6-
import { processMessage } from './assistantEngine';
73
import ChatButton from './ChatButton';
84
import ChatModal from './ChatModal';
9-
10-
const CHAT_WELCOME_MESSAGES = {
11-
en: "Hello! I'm SortBot, your sorting algorithm assistant. How can I help you today?",
12-
es: 'Hola. Soy SortBot, tu asistente de algoritmos de ordenamiento. ¿En qué te ayudo hoy?',
13-
hi: 'नमस्ते। मैं SortBot हूँ, आपका sorting algorithm assistant। आज मैं आपकी कैसे मदद कर सकता हूँ?',
14-
fr: "Bonjour. Je suis SortBot, votre assistant d'algorithmes de tri. Comment puis-je vous aider aujourd'hui ?",
15-
de: 'Hallo. Ich bin SortBot, dein Assistent fuer Sortieralgorithmen. Wie kann ich dir heute helfen?',
16-
zh: '你好。我是 SortBot,你的排序算法助手。今天我可以帮你什么?',
17-
bn: 'হ্যালো। আমি SortBot, তোমার sorting algorithm assistant। আজ কীভাবে সাহায্য করতে পারি?',
18-
ja: 'こんにちは。SortBotです。ソートアルゴリズムの学習を手伝います。今日は何を知りたいですか?',
19-
jp: 'こんにちは。SortBotです。ソートアルゴリズムの学習を手伝います。今日は何を知りたいですか?',
20-
};
5+
import { useChatAssistantController } from './hooks/useChatAssistantController';
216

227
export default function ChatAssistant({
238
isOpen: isOpenProp,
249
onClose,
2510
onToggle,
2611
}) {
27-
const [isOpenState, setIsOpenState] = useState(false);
28-
const isOpen = typeof isOpenProp === 'boolean' ? isOpenProp : isOpenState;
29-
const [input, setInput] = useState('');
30-
const [messages, setMessages] = useState([]);
31-
const [isTyping, setIsTyping] = useState(false);
32-
const [typingInterval, setTypingInterval] = useState(null);
33-
const [errorCount, setErrorCount] = useState(0);
34-
const [_retryCount, setRetryCount] = useState(0);
35-
36-
const { getContextObject, addToHistory } = useAlgorithmState();
37-
const { language } = useLanguage();
38-
const { playTypingSound, isAudioEnabled } = useAudio();
3912
const { isMobileOverlayVisible } = useMobileOverlay();
40-
41-
const messagesEndRef = useRef(null);
42-
const lastTypingSoundRef = useRef(0);
43-
44-
// Initialize with welcome message
45-
useEffect(() => {
46-
// Add welcome message with delay
47-
const timer = setTimeout(() => {
48-
setMessages(prevMessages => {
49-
if (prevMessages.length > 0) return prevMessages;
50-
return [
51-
{
52-
role: 'model',
53-
content:
54-
CHAT_WELCOME_MESSAGES[language] || CHAT_WELCOME_MESSAGES.en,
55-
},
56-
];
57-
});
58-
}, 1000);
59-
60-
return () => clearTimeout(timer);
61-
}, [language]);
62-
63-
// Scroll to bottom on new messages
64-
useEffect(() => {
65-
const scrollToBottom = () => {
66-
messagesEndRef.current?.scrollIntoView({
67-
behavior: 'smooth',
68-
block: 'end',
69-
});
70-
};
71-
72-
if (messages.length > 0) {
73-
scrollToBottom();
74-
// Double-check scroll position after any images/content loads
75-
setTimeout(scrollToBottom, 100);
76-
}
77-
}, [messages]);
78-
79-
// Clean up typing interval on unmount
80-
useEffect(() => {
81-
return () => {
82-
if (typingInterval) {
83-
clearInterval(typingInterval);
84-
}
85-
};
86-
}, [typingInterval]);
87-
88-
// Handle chat open/close with animation
89-
const toggleChat = useCallback(() => {
90-
if (typeof isOpenProp === 'boolean') {
91-
// For controlled state, use onToggle if provided, otherwise fallback to onClose
92-
if (onToggle) {
93-
onToggle();
94-
} else if (isOpen && onClose) {
95-
onClose();
96-
}
97-
} else {
98-
setIsOpenState(prev => !prev);
99-
}
100-
101-
// Enable audio interaction and reset error count when opening
102-
if (!isOpen) {
103-
// Enable audio interaction
104-
const event = new MouseEvent('click', {
105-
view: window,
106-
bubbles: true,
107-
cancelable: true,
108-
});
109-
document.dispatchEvent(event);
110-
111-
// Reset error count on new session
112-
setErrorCount(0);
113-
}
114-
}, [isOpen, isOpenProp, onClose, onToggle]);
115-
116-
// Enhanced message display with typing animation
117-
const displayMessageWithTyping = useCallback(
118-
(text, userInput) => {
119-
// Check if this is a pre-computed instant response (contains HTML)
120-
const isInstantResponse =
121-
text.includes('<div') || text.includes('<p class=');
122-
123-
if (isInstantResponse) {
124-
// Show instant response without typing animation
125-
setMessages(prev => [...prev, { role: 'model', content: text }]);
126-
addToHistory({ question: userInput, answer: text });
127-
return;
128-
}
129-
130-
// For other responses, use typing animation
131-
let displayed = '';
132-
let i = 0;
133-
134-
setIsTyping(true);
135-
setMessages(prev => [...prev, { role: 'model', content: '' }]);
136-
137-
const interval = setInterval(() => {
138-
const now = Date.now();
139-
140-
if (i < text.length) {
141-
// Play typing sound with rate limiting
142-
if (now - lastTypingSoundRef.current >= 200 && isAudioEnabled) {
143-
playTypingSound();
144-
lastTypingSoundRef.current = now;
145-
}
146-
147-
displayed += text[i];
148-
i++;
149-
150-
setMessages(prev => {
151-
const last = prev[prev.length - 1];
152-
if (last.role === 'model') {
153-
return [...prev.slice(0, -1), { ...last, content: displayed }];
154-
}
155-
return prev;
156-
});
157-
} else {
158-
clearInterval(interval);
159-
setTypingInterval(null);
160-
setIsTyping(false);
161-
addToHistory({ question: userInput, answer: text });
162-
}
163-
}, 20); // Faster typing for better responsiveness
164-
165-
setTypingInterval(interval);
166-
},
167-
[isAudioEnabled, playTypingSound, addToHistory]
168-
);
169-
170-
// Enhanced error handling with better user feedback
171-
const handleError = useCallback(
172-
error => {
173-
console.error('Error: Chat Error:', error);
174-
setErrorCount(prev => prev + 1);
175-
176-
let errorMessage = '';
177-
178-
if (error.message?.includes('TIMEOUT_ERROR')) {
179-
errorMessage = `
180-
<div class="animate-fade-in space-y-1 max-w-full">
181-
<p class="m-0 text-orange-400"> Request Timeout</p>
182-
<p class="m-0 text-sm">The request took too long to process. Let me help you with local knowledge instead!</p>
183-
<p class="m-0 text-xs text-blue-300">Tip: Try asking about specific algorithms!</p>
184-
</div>`;
185-
} else if (error.message?.includes('NETWORK_ERROR')) {
186-
errorMessage = `
187-
<div class="animate-fade-in space-y-1 max-w-full">
188-
<p class="m-0 text-yellow-400"> Connection Issue</p>
189-
<p class="m-0 text-sm">I'm having trouble connecting. Let me help you with local knowledge instead!</p>
190-
<p class="m-0 text-xs text-blue-300">Tip: Try asking about specific algorithms!</p>
191-
</div>`;
192-
} else if (error.message?.includes('RATE_LIMIT')) {
193-
errorMessage = `
194-
<div class="animate-fade-in space-y-1 max-w-full">
195-
<p class="m-0 text-orange-400"> Rate Limit Reached</p>
196-
<p class="m-0 text-sm">I'm getting too many requests. Please wait a moment and try again!</p>
197-
<p class="m-0 text-xs text-blue-300">Tip: In the meantime, try exploring the algorithms above!</p>
198-
</div>`;
199-
} else if (error.message?.includes('SERVER_ERROR')) {
200-
errorMessage = `
201-
<div class="animate-fade-in space-y-1 max-w-full">
202-
<p class="m-0 text-red-400"> Server Issue</p>
203-
<p class="m-0 text-sm">There's a temporary server issue. Let me help you with local knowledge instead!</p>
204-
<p class="m-0 text-xs text-blue-300">Tip: Try asking about specific algorithms!</p>
205-
</div>`;
206-
} else {
207-
errorMessage =
208-
errorCount > 2
209-
? `
210-
<div class="animate-fade-in space-y-1 max-w-full">
211-
<p class="m-0 text-red-400"> Persistent Issue</p>
212-
<p class="m-0 text-sm">I'm having trouble connecting. Please try again later or refresh the page.</p>
213-
<p class="m-0 text-xs text-blue-300">Tip: In the meantime, explore the algorithms above!</p>
214-
</div>`
215-
: `
216-
<div class="animate-fade-in space-y-1 max-w-full">
217-
<p class="m-0 text-yellow-400"> Temporary Issue</p>
218-
<p class="m-0 text-sm">I encountered an error. Let me try to help you again.</p>
219-
<p class="m-0 text-xs text-blue-300">Tip: Try asking about specific algorithms!</p>
220-
</div>`;
221-
}
222-
223-
setMessages(prev => [
224-
...prev,
225-
{
226-
role: 'error',
227-
content: errorMessage,
228-
},
229-
]);
230-
231-
setIsTyping(false);
232-
},
233-
[errorCount]
234-
);
235-
236-
// Enhanced message sending with validation and retry mechanism
237-
const handleSend = useCallback(
238-
async (retryAttempt = 0) => {
239-
const trimmedInput = input.trim();
240-
if (!trimmedInput || isTyping) return;
241-
242-
// Clear previous interval if exists
243-
if (typingInterval) {
244-
clearInterval(typingInterval);
245-
setTypingInterval(null);
246-
}
247-
248-
setInput('');
249-
setMessages(prev => [...prev, { role: 'user', content: trimmedInput }]);
250-
251-
// Show immediate feedback for instant responses
252-
const isInstantQuery =
253-
/^(support|creator|github|help|thank|hi|hello)$/i.test(trimmedInput);
254-
if (isInstantQuery) {
255-
// Add a brief loading indicator
256-
setMessages(prev => [
257-
...prev,
258-
{
259-
role: 'model',
260-
content: '<div class="animate-pulse text-slate-400">...</div>',
261-
},
262-
]);
263-
}
264-
265-
try {
266-
const context = {
267-
...getContextObject(),
268-
uiLanguage: language || 'en',
269-
};
270-
if (process.env.NODE_ENV === 'development') {
271-
console.log('Context: Context passed to assistant:', context);
272-
}
273-
274-
const result = await processMessage(trimmedInput, context);
275-
276-
if (result.type === 'response') {
277-
// Remove loading indicator if it exists
278-
setMessages(prev => {
279-
const filtered = prev.filter(msg => !msg.content.includes('...'));
280-
return filtered;
281-
});
282-
283-
displayMessageWithTyping(result.content, trimmedInput);
284-
setRetryCount(0); // Reset retry count on success
285-
} else {
286-
handleError(new Error('Invalid response type'));
287-
}
288-
} catch (error) {
289-
// Remove loading indicator on error
290-
setMessages(prev => {
291-
const filtered = prev.filter(msg => !msg.content.includes('...'));
292-
return filtered;
293-
});
294-
295-
// Retry logic for certain errors
296-
if (
297-
retryAttempt < 2 &&
298-
(error.message?.includes('NETWORK_ERROR') ||
299-
error.message?.includes('TIMEOUT_ERROR') ||
300-
error.message?.includes('SERVER_ERROR'))
301-
) {
302-
setRetryCount(prev => prev + 1);
303-
setMessages(prev => [
304-
...prev,
305-
{
306-
role: 'error',
307-
content: `
308-
<div class="animate-fade-in space-y-1 max-w-full">
309-
<p class="m-0 text-yellow-400"> Retrying... (${retryAttempt + 1}/2)</p>
310-
<p class="m-0 text-sm">Let me try that again for you.</p>
311-
</div>`,
312-
},
313-
]);
314-
315-
// Wait a bit before retrying
316-
setTimeout(
317-
() => {
318-
handleSend(retryAttempt + 1);
319-
},
320-
1000 * (retryAttempt + 1)
321-
); // Exponential backoff
322-
} else {
323-
handleError(error);
324-
}
325-
}
326-
},
327-
[
328-
input,
329-
isTyping,
330-
typingInterval,
331-
language,
332-
getContextObject,
333-
displayMessageWithTyping,
334-
handleError,
335-
]
336-
);
13+
const {
14+
controlledIsOpen,
15+
setIsOpenState,
16+
input,
17+
setInput,
18+
messages,
19+
isTyping,
20+
messagesEndRef,
21+
toggleChat,
22+
handleSend,
23+
} = useChatAssistantController({
24+
isOpenProp,
25+
onClose,
26+
onToggle,
27+
});
28+
const isOpen = controlledIsOpen;
33729

33830
if (isMobileOverlayVisible) return null;
33931

SortVision/src/components/chatbot/assistantEngine.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,18 @@ export async function processMessage(query, context) {
126126
}
127127

128128
const mentionedAlgorithms = extractAlgorithms(cleanQuery);
129+
const looksLikeRecommendationQuery =
130+
/\b(best|which|recommend|fastest|optimal)\b/.test(lowerCaseQuery);
131+
const looksLikeSpecificAlgorithmLookup =
132+
/\b(what is|explain|how does|tell me about|algorithm)\b/.test(
133+
lowerCaseQuery
134+
);
129135
const looksLikeUnknownSortQuery =
130136
isEnglishMode &&
131137
lowerCaseQuery.includes('sort') &&
132138
mentionedAlgorithms.length === 0 &&
139+
looksLikeSpecificAlgorithmLookup &&
140+
!looksLikeRecommendationQuery &&
133141
!containsKeyword(lowerCaseQuery, KEYWORDS.comparison) &&
134142
!containsKeyword(lowerCaseQuery, KEYWORDS.complexity);
135143

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const CHAT_WELCOME_MESSAGES = {
2+
en: "Hello! I'm SortBot, your sorting algorithm assistant. How can I help you today?",
3+
es: 'Hola. Soy SortBot, tu asistente de algoritmos de ordenamiento. ¿En qué te ayudo hoy?',
4+
hi: 'नमस्ते। मैं SortBot हूँ, आपका sorting algorithm assistant। आज मैं आपकी कैसे मदद कर सकता हूँ?',
5+
fr: "Bonjour. Je suis SortBot, votre assistant d'algorithmes de tri. Comment puis-je vous aider aujourd'hui ?",
6+
de: 'Hallo. Ich bin SortBot, dein Assistent fuer Sortieralgorithmen. Wie kann ich dir heute helfen?',
7+
zh: '你好。我是 SortBot,你的排序算法助手。今天我可以帮你什么?',
8+
bn: 'হ্যালো। আমি SortBot, তোমার sorting algorithm assistant। আজ কীভাবে সাহায্য করতে পারি?',
9+
ja: 'こんにちは。SortBotです。ソートアルゴリズムの学習を手伝います。今日は何を知りたいですか?',
10+
jp: 'こんにちは。SortBotです。ソートアルゴリズムの学習を手伝います。今日は何を知りたいですか?',
11+
};

0 commit comments

Comments
 (0)