|
import { useEffect, useRef, useState } from "react"; |
|
import { Link } from "react-router-dom"; |
|
import ChatInterface from "@/components/ChatInterface"; |
|
import ControlPanel from "@/components/ControlPanel"; |
|
import ChatSidebar from "@/components/ChatSidebar"; |
|
import VoiceChat from "@/components/VoiceChat"; |
|
import { v4 as uuidv4 } from "uuid"; |
|
import { |
|
Mic, |
|
MicOff, |
|
Settings, |
|
Menu, |
|
PanelLeft, |
|
PanelLeftClose, |
|
ArrowLeft, |
|
} from "lucide-react"; |
|
import { Button } from "@/components/ui/button"; |
|
import { useAppSelector, useAppDispatch } from "@/redux/hooks"; |
|
import { setIsListening, setSidebarVisibility } from "@/redux/slices/chatSlice"; |
|
import { |
|
setActiveChat, |
|
createChat, |
|
renameChat, |
|
} from "@/redux/slices/chatsSlice"; |
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; |
|
import { useIsMobile } from "@/hooks/use-mobile"; |
|
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"; |
|
import { useToast } from "@/hooks/use-toast"; |
|
|
|
const Index = () => { |
|
const dispatch = useAppDispatch(); |
|
const { activeChatMessages, isListening, isSidebarVisible, isChangingChat } = |
|
useAppSelector((state) => state.chat); |
|
const { chats, activeChat } = useAppSelector((state) => state.chats); |
|
const { activeVoice, temperature, maxTokens, embeddingsText } = |
|
useAppSelector((state) => state.settings); |
|
|
|
const isMobile = useIsMobile(); |
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false); |
|
const [isChatSidebarOpen, setIsChatSidebarOpen] = useState(false); |
|
const [isVoiceChatMode, setIsVoiceChatMode] = useState(true); |
|
const { toast } = useToast(); |
|
const [sessionId] = useState(() => { |
|
const savedSessionId = localStorage.getItem("voiceSessionId"); |
|
return savedSessionId || uuidv4(); |
|
}); |
|
|
|
const toggleListening = () => { |
|
if (isVoiceChatMode) return; |
|
|
|
if (chats.length === 0) { |
|
dispatch(createChat(sessionId)); |
|
const chatId = sessionId; |
|
dispatch(setActiveChat(chatId)); |
|
toast({ |
|
title: "New chat created", |
|
description: "Start a fresh conversation with your AI assistant.", |
|
}); |
|
} |
|
|
|
dispatch(setIsListening(!isListening)); |
|
}; |
|
|
|
const handleShowSidebar = () => { |
|
dispatch(setSidebarVisibility(true)); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
const renderSettingsPanel = () => { |
|
if (isListening && !isVoiceChatMode) { |
|
return null; |
|
} |
|
|
|
const settingsContent = <ControlPanel />; |
|
|
|
if (isMobile) { |
|
return ( |
|
<Drawer open={isDrawerOpen} onOpenChange={setIsDrawerOpen}> |
|
<DrawerTrigger asChild> |
|
<Button |
|
variant="outline" |
|
size="icon" |
|
className="fixed bottom-20 right-4 z-10 rounded-full bg-black border border-white/40 text-white shadow-lg" |
|
> |
|
<Settings className="h-5 w-5" /> |
|
</Button> |
|
</DrawerTrigger> |
|
<DrawerContent className="h-[85vh] bg-black"> |
|
{settingsContent} |
|
</DrawerContent> |
|
</Drawer> |
|
); |
|
} |
|
|
|
return <div className="hidden md:block">{settingsContent}</div>; |
|
}; |
|
|
|
const renderChatSidebar = () => { |
|
if (isMobile) { |
|
return ( |
|
<Sheet open={isChatSidebarOpen} onOpenChange={setIsChatSidebarOpen}> |
|
<SheetTrigger asChild> |
|
<Button |
|
variant="outline" |
|
size="icon" |
|
onClick={() => { |
|
setIsChatSidebarOpen(true); |
|
}} |
|
className="fixed top-16 left-4 rounded-full bg-black border border-white/40 text-white shadow-lg hover:cursor-pointer z-50" |
|
> |
|
<Menu className="h-5 w-5" /> |
|
</Button> |
|
</SheetTrigger> |
|
<SheetContent |
|
side="left" |
|
className="p-0 w-full max-w-md bg-black border-white/10" |
|
> |
|
<ChatSidebar |
|
isMobileSidebar={true} |
|
onClose={() => setIsChatSidebarOpen(false)} |
|
/> |
|
</SheetContent> |
|
</Sheet> |
|
); |
|
} |
|
|
|
return ( |
|
<div |
|
className={`transition-all duration-300 ease-in-out ${ |
|
isSidebarVisible ? "block" : "hidden" |
|
} md:w-64 h-full`} |
|
> |
|
<ChatSidebar /> |
|
</div> |
|
); |
|
}; |
|
|
|
return ( |
|
<div className="flex flex-col md:flex-row h-screen bg-black overflow-hidden"> |
|
{/* {!isListening && ( |
|
<div className="fixed top-4 left-4 z-50 md:top-4 md:left-6"> |
|
<Link to="/"> |
|
<Button |
|
variant="outline" |
|
size="sm" |
|
className="bg-black/80 text-white hover:text-white/90 backdrop-blur-sm border border-white/20 shadow-md hover:bg-white/10 transition-all duration-300 ease-in-out" |
|
> |
|
<ArrowLeft className="h-4 w-4 mr-2 text-white" /> Back to Home |
|
</Button> |
|
</Link> |
|
</div> |
|
)} */} |
|
|
|
{!isListening && renderChatSidebar()} |
|
|
|
<div className="flex-1 flex flex-col h-full md:border-r border-white/10 relative"> |
|
{!isMobile && !isSidebarVisible && ( |
|
<Button |
|
variant="outline" |
|
size="icon" |
|
onClick={handleShowSidebar} |
|
className="fixed top-5 left-[20px] text-white z-50 bg-black/80 backdrop-blur-sm border border-white/20 shadow-md hover:bg-black/60 hover:border-white/60 transition-all duration-300 ease-in-out" |
|
> |
|
<PanelLeft className="h-5 w-5 text-white" /> |
|
</Button> |
|
)} |
|
|
|
{/* {!isMobile && isSidebarVisible && ( |
|
<Button |
|
variant="ghost" |
|
size="icon" |
|
onClick={handleToggleSidebar} |
|
className="absolute top-4 left-[260px] z-50 transition-all duration-300 ease-in-out" |
|
> |
|
<PanelLeftClose className="h-5 w-5" /> |
|
</Button> |
|
)} */} |
|
|
|
<ChatInterface |
|
messages={activeChatMessages} |
|
isListening={isListening} |
|
/> |
|
|
|
<div className="p-6 border-t border-white/10 bg-black/80 backdrop-blur-sm flex justify-center items-center relative overflow-hidden"> |
|
<div className="absolute inset-0 flex justify-center items-center pointer-events-none"> |
|
<div |
|
className={`w-40 h-40 rounded-full ${ |
|
isListening ? "bg-white/5" : "bg-white/3" |
|
} blur-3xl transition-all duration-500 ease-in-out`} |
|
></div> |
|
</div> |
|
|
|
{isVoiceChatMode ? ( |
|
<div className="w-full max-w-md"> |
|
<VoiceChat activeVoice={activeVoice} temperature={temperature} /> |
|
</div> |
|
) : ( |
|
<Button |
|
variant="outline" |
|
size="icon" |
|
className={`rounded-full w-16 h-16 shadow-md relative z-10 transition-all duration-300 ${ |
|
isListening |
|
? "bg-white text-black hover:bg-white/90 border-0" |
|
: "bg-black text-white border border-white/40 hover:bg-white/10" |
|
}`} |
|
onClick={toggleListening} |
|
> |
|
{isListening ? ( |
|
<MicOff className="h-6 w-6" /> |
|
) : ( |
|
<Mic className="h-6 w-6" /> |
|
)} |
|
</Button> |
|
)} |
|
</div> |
|
|
|
{/* Toggle between voice chat and text chat modes */} |
|
{/* <div className="absolute bottom-24 left-1/2 -translate-x-1/2 z-20"> |
|
<div className="flex items-center p-1 bg-gray-900/70 backdrop-blur-sm rounded-full border border-white/10"> |
|
<Button |
|
size="sm" |
|
variant={isVoiceChatMode ? "ghost" : "default"} |
|
className={`rounded-full text-xs px-3 ${!isVoiceChatMode ? "" : "bg-transparent text-white/70 hover:text-white hover:bg-white/10"}`} |
|
onClick={() => setIsVoiceChatMode(false)} |
|
> |
|
Text Chat |
|
</Button> |
|
<Button |
|
size="sm" |
|
variant={isVoiceChatMode ? "default" : "ghost"} |
|
className={`rounded-full text-xs px-3 ${isVoiceChatMode ? "" : "bg-transparent text-white/70 hover:text-white hover:bg-white/10"}`} |
|
onClick={() => setIsVoiceChatMode(true)} |
|
> |
|
Voice Chat |
|
</Button> |
|
</div> |
|
</div> */} |
|
</div> |
|
|
|
{!isListening && activeChat && renderSettingsPanel()} |
|
</div> |
|
); |
|
}; |
|
|
|
export default Index; |
|
|