turash/bugulma/frontend/hooks/useChat.ts
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +01:00

105 lines
3.2 KiB
TypeScript

import { useCallback, useEffect, useMemo, useState } from 'react';
import { ChatMessage } from '@/types.ts';
import { useTranslation } from '@/hooks/useI18n.tsx';
import { sendChatMessage } from '@/services/chat-api.ts';
const CHAT_HISTORY_KEY = 'chat-history';
export const useChat = () => {
const { t } = useTranslation();
const geminiSystemInstruction = t('gemini.chatSystemInstruction');
const initialMessage: ChatMessage = useMemo(
() => ({
id: 'init',
role: 'model',
text: t('chatbot.initialMessage'),
}),
[t]
);
const [messages, setMessages] = useState<ChatMessage[]>(() => {
try {
const storedMessages = window.localStorage.getItem(CHAT_HISTORY_KEY);
if (storedMessages && JSON.parse(storedMessages).length > 0) {
return JSON.parse(storedMessages);
}
return [initialMessage];
} catch (error) {
console.error('Failed to load chat history from localStorage', error);
return [initialMessage];
}
});
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
try {
window.localStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(messages));
} catch (error) {
console.error('Failed to save chat history to localStorage', error);
}
}, [messages]);
const sendMessage = useCallback(
async (payload: { text: string; imageUrl?: string }) => {
if (!payload.text.trim() && !payload.imageUrl) return;
const userMessage: ChatMessage = {
id: `user-${Date.now()}`,
role: 'user',
text: payload.text,
imageUrl: payload.imageUrl,
};
const modelMessageId = `model-${Date.now()}`;
const placeholderModelMessage: ChatMessage = {
id: modelMessageId,
role: 'model',
text: '',
isLoading: true,
};
setMessages((prev) => [...prev, userMessage, placeholderModelMessage]);
setIsLoading(true);
setError(null);
try {
// Build message with image if provided
const messageText = payload.imageUrl ? `${payload.text}\n[Image attached]` : payload.text;
const response = await sendChatMessage({
message: messageText,
system_instruction: geminiSystemInstruction,
});
setMessages((prev) =>
prev.map((msg) => (msg.id === modelMessageId ? { ...msg, text: response } : msg))
);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : 'An unknown error occurred';
setError(errorMessage);
const errorResponseMessage: ChatMessage = {
id: `model-error-${Date.now()}`,
role: 'model',
text: t('chatbot.errorMessage'),
};
setMessages((prev) => [...prev.slice(0, -1), errorResponseMessage]);
} finally {
setIsLoading(false);
setMessages((prev) =>
prev.map((msg) => (msg.id === modelMessageId ? { ...msg, isLoading: false } : msg))
);
}
},
[geminiSystemInstruction, t]
);
const clearChat = useCallback(() => {
setMessages([initialMessage]);
}, [initialMessage]);
return { messages, isLoading, error, sendMessage, clearChat };
};