import { useState, useCallback, useEffect } from "react";
import {
getMeshChatHistory,
clearMeshChatHistory,
chatWithMeshContext,
type MeshChatMessageRecord,
type MeshChatContext,
type MeshChatResponse,
type LlmModel,
} from "../lib/api";
export interface MeshChatState {
conversationId: string | null;
messages: MeshChatMessageRecord[];
loading: boolean;
error: string | null;
sending: boolean;
}
export function useMeshChatHistory() {
const [state, setState] = useState<MeshChatState>({
conversationId: null,
messages: [],
loading: true,
error: null,
sending: false,
});
const fetchHistory = useCallback(async () => {
setState((prev) => ({ ...prev, loading: true, error: null }));
try {
const response = await getMeshChatHistory();
setState((prev) => ({
...prev,
conversationId: response.conversationId,
messages: response.messages,
loading: false,
}));
} catch (e) {
setState((prev) => ({
...prev,
error: e instanceof Error ? e.message : "Failed to fetch chat history",
loading: false,
}));
}
}, []);
const clearHistory = useCallback(async (): Promise<boolean> => {
setState((prev) => ({ ...prev, loading: true, error: null }));
try {
const response = await clearMeshChatHistory();
setState({
conversationId: response.conversationId,
messages: [],
loading: false,
error: null,
sending: false,
});
return true;
} catch (e) {
setState((prev) => ({
...prev,
error: e instanceof Error ? e.message : "Failed to clear chat history",
loading: false,
}));
return false;
}
}, []);
const sendMessage = useCallback(
async (
message: string,
context: MeshChatContext,
model?: LlmModel
): Promise<MeshChatResponse | null> => {
setState((prev) => ({ ...prev, sending: true, error: null }));
// Optimistically add user message (will be refetched after response)
const tempUserMessage: MeshChatMessageRecord = {
id: `temp-${Date.now()}`,
conversationId: state.conversationId || "",
role: "user",
content: message,
contextType: context.type,
contextTaskId: context.taskId || null,
toolCalls: null,
pendingQuestions: null,
createdAt: new Date().toISOString(),
};
setState((prev) => ({
...prev,
messages: [...prev.messages, tempUserMessage],
}));
try {
const response = await chatWithMeshContext(message, context, model);
// Refetch to get the actual saved messages (with proper IDs)
await fetchHistory();
setState((prev) => ({ ...prev, sending: false }));
return response;
} catch (e) {
// Remove optimistic message on error
setState((prev) => ({
...prev,
messages: prev.messages.filter((m) => m.id !== tempUserMessage.id),
error: e instanceof Error ? e.message : "Failed to send message",
sending: false,
}));
return null;
}
},
[state.conversationId, fetchHistory]
);
// Initial fetch on mount
useEffect(() => {
fetchHistory();
}, [fetchHistory]);
return {
conversationId: state.conversationId,
messages: state.messages,
loading: state.loading,
error: state.error,
sending: state.sending,
fetchHistory,
clearHistory,
sendMessage,
};
}