1+ import { useState , useRef , useEffect } from 'react' ;
12import { useParams , Link } from 'react-router' ;
23import { useAppContext } from '../context/AppContext' ;
34
5+ // Mock AI responses as fallback
6+ const ALIEN_RESPONSES = [
7+ "My optic sensors appreciate your visual symmetry. Shall we exchange genetic material samples?" ,
8+ "Your carbon-based form is adequate. How many standard galactic rotations have you survived?" ,
9+ "I observe you only have two manipulating appendages. How do you efficiently consume nutrient paste?" ,
10+ "Your transmission implies affectionate intent. My emotion-processing subroutines are currently downloading an update." ,
11+ "Interesting. Most species on my planet communicate via scent-gland excretion. Your vocal vibrations are... quaint." ,
12+ "Warning: Your bio-signature is dangerously attractive. Please lower your gravitational pull." ,
13+ "Are you emitting pheromones or is my atmospheric analyzer malfunctioning?" ,
14+ ] ;
15+
16+ type Message = {
17+ id : string ;
18+ sender : 'user' | 'alien' ;
19+ text : string ;
20+ } ;
21+
422export default function Chat ( ) {
523 const { id } = useParams ( ) ;
624 const { matches } = useAppContext ( ) ;
725
26+ const [ messages , setMessages ] = useState < Message [ ] > ( [ ] ) ;
27+ const [ inputValue , setInputValue ] = useState ( '' ) ;
28+ const [ isTranslating , setIsTranslating ] = useState ( false ) ;
29+ const messagesEndRef = useRef < HTMLDivElement > ( null ) ;
30+
831 const alien = matches . find ( m => m . id === id ) ;
932
33+ const scrollToBottom = ( ) => {
34+ messagesEndRef . current ?. scrollIntoView ( { behavior : "smooth" } ) ;
35+ } ;
36+
37+ useEffect ( ( ) => {
38+ scrollToBottom ( ) ;
39+ } , [ messages , isTranslating ] ) ;
40+
41+ // Simulate an AI or API call to get the alien's response
42+ const generateAlienResponse = async ( userText : string , currentMessages : Message [ ] ) => {
43+ // Check if the user has provided a Gemini API Key in their environment variables (optional AI integration)
44+ const geminiApiKey = import . meta. env . VITE_GEMINI_API_KEY ;
45+
46+ if ( geminiApiKey && geminiApiKey !== 'YOUR_GEMINI_API_KEY_HERE' ) {
47+ // Format the last few messages for conversational context
48+ const chatHistory = currentMessages . slice ( - 4 ) . map ( m =>
49+ `${ m . sender === 'user' ? 'Human' : alien ?. name } : ${ m . text } `
50+ ) . join ( '\n' ) ;
51+
52+ const promptText = `You are a user named ${ alien ?. name } on the dating app "ALIGNED".
53+ ${ chatHistory ? `Here is the recent chat history:\n${ chatHistory } \n` : '' }
54+ Human: "${ userText } "
55+
56+ Reply directly to the Human's latest message as ${ alien ?. name } .
57+ The twist: Act like your response went through a VERY CHEAP intergalactic translation software.
58+ You must use VERY SIMPLE, basic English. However, insert exactly 1 or 2 awkwardly literal or slightly confusing words that sound like a funny misunderstanding of human culture. DO NOT be creepy, gross, or overly biological. Keep it harmless and charmingly awkward.
59+
60+ For example:
61+ - Instead of "You have beautiful eyes", say: "Your visual orbs are very shiny... good looking."
62+ - Instead of "I am doing well", say: "I am having an excellent rotation today."
63+ - Instead of "What are you doing?", say: "What hobbies are your human hands performing?"
64+
65+ Keep it friendly, slightly flirtatious, and warmly confusing. Keep the grammar simple like a bad tourist translator, but drop in that one hilariously literal phrase. Maximum 2 sentences. Do not prefix the text with your name.` ;
66+
67+ try {
68+ const response = await fetch ( `https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent?key=${ geminiApiKey } ` , {
69+ method : 'POST' ,
70+ headers : {
71+ 'Content-Type' : 'application/json'
72+ } ,
73+ body : JSON . stringify ( {
74+ contents : [ {
75+ parts : [ { text : promptText } ]
76+ } ]
77+ } )
78+ } ) ;
79+
80+ const data = await response . json ( ) ;
81+ if ( data . error ) {
82+ console . error ( "Gemini API Error:" , data . error ) ;
83+ return `[TRANSLATOR ERROR: ${ data . error . message || "Invalid API parameters" } ]` ;
84+ }
85+
86+ if ( data && data . candidates && data . candidates . length > 0 ) {
87+ return data . candidates [ 0 ] . content . parts [ 0 ] . text ;
88+ }
89+ } catch ( e ) {
90+ console . error ( "AI translation failed:" , e ) ;
91+ return "[TRANSLATOR COMMUNICATION FAILURE: Unable to reach Gemini backend. Did you restart the server?]" ;
92+ }
93+ }
94+
95+ // Fallback ONLY if there is no API key configured at all
96+ return ALIEN_RESPONSES [ Math . floor ( Math . random ( ) * ALIEN_RESPONSES . length ) ] ;
97+ } ;
98+
99+ const handleSendMessage = async ( ) => {
100+ if ( ! inputValue . trim ( ) || isTranslating ) return ;
101+
102+ const userMessage : Message = {
103+ id : Date . now ( ) . toString ( ) ,
104+ sender : 'user' ,
105+ text : inputValue . trim ( )
106+ } ;
107+
108+ setMessages ( prev => [ ...prev , userMessage ] ) ;
109+ setInputValue ( '' ) ;
110+ setIsTranslating ( true ) ;
111+
112+ // Simulate "Processing Translation..." delay
113+ const delay = Math . floor ( Math . random ( ) * 1500 ) + 1500 ; // 1.5s - 3.0s delay
114+
115+ setTimeout ( async ( ) => {
116+ // Pass the previous messages list array AND the new user message
117+ const responseText = await generateAlienResponse ( userMessage . text , [ ...messages , userMessage ] ) ;
118+ const alienMessage : Message = {
119+ id : ( Date . now ( ) + 1 ) . toString ( ) ,
120+ sender : 'alien' ,
121+ text : responseText
122+ } ;
123+
124+ setMessages ( prev => [ ...prev , alienMessage ] ) ;
125+ setIsTranslating ( false ) ;
126+ } , delay ) ;
127+ } ;
128+
10129 if ( ! alien ) {
11130 return (
12131 < div style = { { padding : '40px' , textAlign : 'center' } } >
@@ -18,11 +137,12 @@ export default function Chat() {
18137 }
19138
20139 return (
21- < div style = { { maxWidth : '800px' , margin : '20px auto' , padding : '0 20px' , flex : 1 , display : 'flex' , flexDirection : 'column' } } >
22- < div className = "glass-panel" style = { { display : 'flex' , flexDirection : 'column' , height : 'calc(100vh - 120px)' } } >
140+ < div style = { { width : '100%' , maxWidth : '800px' , margin : '20px auto' , padding : '0 20px' , flex : 1 , display : 'flex' , flexDirection : 'column' } } >
141+ < div className = "glass-panel" style = { { backgroundColor : '#1a1829' , border : 'none' , display : 'flex' , flexDirection : 'column' , height : 'calc(100vh - 120px)' } } >
23142
24143 { /* Chat Header */ }
25- < div style = { { padding : '20px' , borderBottom : '1px solid var(--glass-border)' , display : 'flex' , alignItems : 'center' , gap : '16px' } } >
144+ < div style = { { padding : '20px' , position : 'relative' , display : 'flex' , alignItems : 'center' , gap : '16px' } } >
145+ < div style = { { position : 'absolute' , bottom : 0 , left : '5%' , right : '5%' , height : '1px' , background : 'linear-gradient(90deg, transparent, var(--color-secondary), transparent)' , opacity : 0.5 } } > </ div >
26146 < img src = { alien . profilePic } alt = { alien . name } style = { { width : '50px' , height : '50px' , borderRadius : '50%' , objectFit : 'cover' } } />
27147 < div >
28148 < h3 style = { { margin : 0 , color : 'var(--color-primary)' } } > { alien . name } </ h3 >
@@ -31,16 +151,64 @@ export default function Chat() {
31151 </ div >
32152
33153 { /* Chat Messages */ }
34- < div style = { { flex : 1 , padding : '20px' , overflowY : 'auto' } } >
35- < div style = { { textAlign : 'center' , margin : '20px 0' , color : 'rgba(234, 222, 218, 0.4)' , fontSize : '0.9rem' } } >
36- Connection established across { alien . distanceAU } AU. Say hello!
37- </ div >
154+ < div style = { { flex : 1 , padding : '20px' , overflowY : 'auto' , display : 'flex' , flexDirection : 'column' , gap : '16px' } } >
155+ { messages . length === 0 && (
156+ < div style = { { textAlign : 'center' , margin : '20px 0' , color : 'rgba(234, 222, 218, 0.4)' , fontSize : '0.9rem' } } >
157+ Connection established across { alien . distanceAU } AU. Say hello!
158+ </ div >
159+ ) }
160+
161+ { messages . map ( ( msg ) => (
162+ < div
163+ key = { msg . id }
164+ style = { {
165+ alignSelf : msg . sender === 'user' ? 'flex-end' : 'flex-start' ,
166+ maxWidth : '80%' ,
167+ backgroundColor : msg . sender === 'user' ? 'var(--color-secondary)' : '#2A263A' ,
168+ border : `1px solid ${ msg . sender === 'user' ? 'var(--color-secondary)' : '#3F3956' } ` ,
169+ padding : '12px 16px' ,
170+ borderRadius : msg . sender === 'user' ? '16px 16px 0 16px' : '16px 16px 16px 0' ,
171+ color : 'white' ,
172+ boxShadow : msg . sender === 'user' ? '0 4px 12px rgba(217, 3, 104, 0.3)' : '0 4px 12px rgba(0, 0, 0, 0.2)' ,
173+ lineHeight : 1.5 ,
174+ fontWeight : 500
175+ } }
176+ >
177+ { msg . text }
178+ </ div >
179+ ) ) }
180+
181+ { isTranslating && (
182+ < div style = { { alignSelf : 'flex-start' , color : 'var(--color-secondary)' , fontSize : '0.9rem' , fontStyle : 'italic' , display : 'flex' , alignItems : 'center' , gap : '8px' } } >
183+ < span style = { { display : 'inline-block' , width : '12px' , height : '12px' , border : '2px solid var(--color-secondary)' , borderTopColor : 'transparent' , borderRadius : '50%' , animation : 'spin 1s linear infinite' } } > </ span >
184+ Processing Translation...
185+ </ div >
186+ ) }
187+
188+ < div ref = { messagesEndRef } />
38189 </ div >
39190
40191 { /* Chat Input */ }
41- < div style = { { padding : '20px' , borderTop : '1px solid var(--glass-border)' , display : 'flex' , gap : '12px' } } >
42- < input type = "text" placeholder = "Send a transmission..." style = { { flex : 1 } } />
43- < button className = "btn-primary" > Send</ button >
192+ < div style = { { padding : '20px' , position : 'relative' , display : 'flex' , gap : '12px' } } >
193+ < div style = { { position : 'absolute' , top : 0 , left : '5%' , right : '5%' , height : '1px' , background : 'linear-gradient(90deg, transparent, var(--color-secondary), transparent)' , opacity : 0.5 } } > </ div >
194+ < input
195+ type = "text"
196+ placeholder = "Send a transmission..."
197+ style = { { flex : 1 , backgroundColor : 'rgba(255, 255, 255, 0.05)' , border : '1px solid var(--glass-border)' , borderRadius : '8px' , padding : '10px 16px' , color : 'white' } }
198+ value = { inputValue }
199+ onChange = { ( e ) => setInputValue ( e . target . value ) }
200+ onKeyDown = { ( e ) => {
201+ if ( e . key === 'Enter' ) handleSendMessage ( ) ;
202+ } }
203+ disabled = { isTranslating }
204+ />
205+ < button
206+ className = "btn-primary"
207+ onClick = { handleSendMessage }
208+ disabled = { ! inputValue . trim ( ) || isTranslating }
209+ >
210+ Send
211+ </ button >
44212 </ div >
45213
46214 </ div >
0 commit comments