11"use client" ;
22
33import { useState , useRef , useEffect } from "react" ;
4+ import { random } from "lodash" ;
45import { Input } from "@/common/components/ui/input" ;
56import { Button } from "@/common/components/ui/button" ;
67import { ScrollArea } from "@/common/components/ui/scroll-area" ;
@@ -9,13 +10,7 @@ import { Send, Search } from "lucide-react";
910import { useGame } from "@/core/store/game-store" ;
1011import { api } from "@/api/api" ;
1112
12- interface Message {
13- id : number ;
14- characterId : number ;
15- content : string ;
16- timestamp : number ;
17- isPlayer : boolean ;
18- }
13+ import { Message } from "@/core/core.types" ;
1914
2015export function Chat ( ) {
2116 const { gameState } = useGame ( ) ;
@@ -25,6 +20,7 @@ export function Chat() {
2520 )
2621 ) ;
2722 const [ filteredCharacters , setFilteredCharacters ] = useState ( characters ) ;
23+ const [ isTyping , setIsTyping ] = useState ( false ) ;
2824
2925 useEffect ( ( ) => {
3026 setCharacters (
@@ -40,23 +36,7 @@ export function Chat() {
4036 return date . toLocaleTimeString ( [ ] , { hour : "2-digit" , minute : "2-digit" } ) ;
4137 } ;
4238
43- // Initial conversations with each character
44- const initialConversations : Record < string , Message [ ] > = { } ;
45-
46- characters . forEach ( ( character ) => {
47- initialConversations [ character . id ] = [
48- {
49- id : Date . now ( ) + Math . random ( ) ,
50- characterId : character . id ,
51- content : "Hi" ,
52- timestamp : useGame . getState ( ) . gameState . world ?. time ?? 0 ,
53- isPlayer : false ,
54- } ,
55- ] ;
56- } ) ;
57-
5839 const [ selectedCharacter , setSelectedCharacter ] = useState ( characters [ 0 ] ) ;
59- const [ conversations , setConversations ] = useState ( initialConversations ) ;
6040 const [ newMessage , setNewMessage ] = useState ( "" ) ;
6141 const [ searchQuery , setSearchQuery ] = useState ( "" ) ;
6242 const scrollAreaRef = useRef < HTMLDivElement > ( null ) ;
@@ -78,32 +58,31 @@ export function Chat() {
7858 const scrollContainer = scrollAreaRef . current ;
7959 scrollContainer . scrollTop = scrollContainer . scrollHeight ;
8060 }
81- } , [ conversations , selectedCharacter ] ) ;
61+ } , [ gameState . chats , selectedCharacter ] ) ;
8262
8363 const handleSendMessage = ( ) => {
8464 if ( newMessage . trim ( ) === "" ) return ;
85-
86- // Add player message
8765 const playerMsg : Message = {
88- id : Date . now ( ) ,
89- characterId : 0 ,
66+ id : useGame . getState ( ) . gameState . world . time ! ,
67+ characterId : selectedCharacter . id ,
9068 content : newMessage ,
91- timestamp : Date . now ( ) ,
69+ timestamp : useGame . getState ( ) . gameState . world . time ! ,
9270 isPlayer : true ,
9371 } ;
94-
95- // Update the conversation with the selected character
96- setConversations ( ( prev ) => ( {
97- ...prev ,
98- [ selectedCharacter . id ] : [
99- ...( prev [ selectedCharacter . id ] || [ ] ) ,
100- playerMsg ,
101- ] ,
102- } ) ) ;
103-
72+ api . character . addChatMessage ( selectedCharacter . id , playerMsg ) ;
10473 setNewMessage ( "" ) ;
10574 } ;
10675
76+ // Typing animation when player sends a message
77+ useEffect ( ( ) => {
78+ if ( ! isTyping ) return ;
79+ const typingDelay = random ( 2000 , 5000 ) ;
80+ const timeout = setTimeout ( ( ) => {
81+ setIsTyping ( false ) ;
82+ } , typingDelay ) ;
83+ return ( ) => clearTimeout ( timeout ) ;
84+ } , [ isTyping ] ) ;
85+
10786 console . log ( "Characters" , characters ) ;
10887 console . log ( "Filtered Characters" , filteredCharacters ) ;
10988
@@ -183,7 +162,8 @@ export function Chat() {
183162
184163 < ScrollArea className = "flex-1 p-4" ref = { scrollAreaRef } >
185164 < div className = "space-y-6" >
186- { conversations [ selectedCharacter . id ] ?. map ( ( message ) => (
165+ { /* Chat messages */ }
166+ { gameState . chats [ selectedCharacter . id ] ?. map ( ( message ) => (
187167 < div
188168 key = { message . id }
189169 className = { `flex gap-4 ${
@@ -204,7 +184,7 @@ export function Chat() {
204184 >
205185 < div
206186 className = { `px-4 py-2 rounded-lg ${
207- message . isPlayer ? "bg-primary " : "bg-muted"
187+ message . isPlayer ? "bg-popover " : "bg-muted"
208188 } `}
209189 >
210190 < p className = "text-sm" > { message . content } </ p >
@@ -220,6 +200,28 @@ export function Chat() {
220200 ) }
221201 </ div >
222202 ) ) }
203+ { /* Typing animation for character (left side only) */ }
204+ { isTyping && (
205+ < div className = "flex gap-4 justify-start" >
206+ < Avatar >
207+ < AvatarFallback >
208+ { api . character . getInitial ( selectedCharacter ) }
209+ </ AvatarFallback >
210+ </ Avatar >
211+ < div className = "flex flex-col max-w-[70%] items-start" >
212+ < div className = "px-4 py-2 rounded-lg bg-muted flex items-center" >
213+ < span className = "animate-pulse flex gap-1" >
214+ < span className = "w-2 h-2 bg-gray-400 rounded-full inline-block" > </ span >
215+ < span className = "w-2 h-2 bg-gray-400 rounded-full inline-block" > </ span >
216+ < span className = "w-2 h-2 bg-gray-400 rounded-full inline-block" > </ span >
217+ </ span >
218+ </ div >
219+ < div className = "text-xs text-gray-500 dark:text-gray-400 mt-1 px-1" >
220+ Typing...
221+ </ div >
222+ </ div >
223+ </ div >
224+ ) }
223225 </ div >
224226 </ ScrollArea >
225227
0 commit comments