deveshm8's picture
added md format and streaming
259cdfe
import ArrowRightAltIcon from "@mui/icons-material/ArrowRightAlt";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
import GraphicEqIcon from "@mui/icons-material/GraphicEq";
import PersonIcon from "@mui/icons-material/Person";
import { Box, IconButton, InputBase, Menu, MenuItem } from "@mui/material";
import React, { useState, useContext, useEffect, useCallback } from "react";
import ReactMarkdown from "react-markdown";
import { useNavigate } from "react-router-dom";
import remarkGfm from "remark-gfm";
import { AuthContext } from "../../../context/AuthContext";
import useWebSocket from "../../../hooks/useTextWebhook";
import { ICreateConversationResponse } from "../../../interfaces/conversation";
import { logout } from "../../../services/api/authService";
import { createConversation } from "../../../services/api/chatService";
interface ChatScreenProps {
isOpen: boolean;
toggleSidebar: () => void;
}
const ChatScreen: React.FC<ChatScreenProps> = ({ isOpen, toggleSidebar }) => {
const [input, setInput] = useState<string>("");
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const { messages, sendMessage, connectWebSocket } = useWebSocket();
const authContext = useContext(AuthContext);
const navigate = useNavigate();
useEffect(() => {
const initializeConversation = async () => {
try {
const conv = await createConversation({ modality: "text" });
const sessionData: ICreateConversationResponse = await conv.data;
const conversationId = sessionData.conversation_id;
connectWebSocket(conversationId);
} catch (error) {
console.error("Failed to initialize conversation:", error);
}
};
initializeConversation();
}, [connectWebSocket]);
const handleSendMessage = useCallback(() => {
if (input.trim()) {
try {
sendMessage(input.trim());
setInput("");
} catch (error) {
console.error("Failed to send message:", error);
}
}
}, [input, sendMessage]);
const handleVoiceInput = useCallback(() => {
navigate("/voice-to-voice");
}, [navigate]);
const handleBotIconClick = useCallback((event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleMenuClose = useCallback(() => {
setAnchorEl(null);
}, []);
const handleLogout = useCallback(async () => {
try {
await logout();
} finally {
authContext?.logout();
}
}, [authContext]);
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
height: "100%",
bgcolor: "#f9f9f9",
color: "#333",
}}
>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
px: 2,
py: 1,
borderBottom: "1px solid #e0e0e0",
bgcolor: "#ffffff",
zIndex: 10,
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
{!isOpen && (
<IconButton onClick={toggleSidebar} size="large">
<ArrowRightAltIcon />
</IconButton>
)}
<Box
sx={{
fontSize: "34px",
fontWeight: "bold",
ml: 1,
}}
>
Chat Bot
</Box>
</Box>
<Box>
<IconButton size="large" onClick={handleBotIconClick}>
<PersonIcon sx={{ fontSize: 34, color: "black" }} />
</IconButton>
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleMenuClose}>
<MenuItem onClick={handleLogout}>Logout</MenuItem>
</Menu>
</Box>
</Box>
<Box
sx={{
flexGrow: 1,
overflowY: "auto",
p: 2,
}}
>
{messages.map((msg, index) => (
<Box
key={index}
sx={{
display: "flex",
alignItems: "center",
justifyContent: msg.sender === "user" ? "flex-end" : "flex-start",
my: 1,
}}
>
{msg.sender === "bot" && (
<Box sx={{ mr: 1 }}>
<PersonIcon sx={{ fontSize: 30, color: "#ccc" }} />
</Box>
)}
<Box
sx={{
maxWidth: "70%",
p: "10px 15px",
borderRadius: "20px",
bgcolor: msg.sender === "user" ? "#e0e0e0" : "#f5f5f5",
color: msg.sender === "user" ? "#333" : "#555",
}}
>
{msg.sender === "bot" ? (
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{`${msg.text}${!msg.isComplete ? "▊" : ""}`}
</ReactMarkdown>
) : (
msg.text
)}
</Box>
</Box>
))}
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
p: 2,
borderTop: "1px solid #e0e0e0",
bgcolor: "#ffffff",
position: "sticky",
bottom: 0,
zIndex: 10,
}}
>
<InputBase
fullWidth
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSendMessage()}
placeholder="Type a message..."
sx={{
flex: 1,
px: 2,
py: 1,
border: "1px solid #ddd",
borderRadius: "20px",
bgcolor: "#f9f9f9",
"& input::placeholder": {
color: "#aaa",
},
}}
/>
<IconButton
onClick={input.length === 0 ? handleVoiceInput : handleSendMessage}
sx={{
ml: 1,
bgcolor: "text.primary",
color: "white",
"&:hover": {
bgcolor: "darkgray",
},
padding: "5px",
borderRadius: "8px",
}}
>
{input.length === 0 ? <GraphicEqIcon /> : <ArrowUpwardIcon />}
</IconButton>
</Box>
</Box>
);
};
export default ChatScreen;