add experimental chat
This commit is contained in:
42
src/components/Gemini.css
Normal file
42
src/components/Gemini.css
Normal file
@@ -0,0 +1,42 @@
|
||||
.user-chat-input {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.user-message {
|
||||
background-color: #aaffaa;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.other-message {
|
||||
background-color: #aaaaff;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.typing-animation {
|
||||
overflow: hidden; /* Hide overflowing text */
|
||||
border-right: 0.1em solid #000; /* Add a border to simulate the cursor */
|
||||
white-space: nowrap; /* Ensure the text stays in one line */
|
||||
animation: typing 1s steps(40, end), blink-caret 0.5s step-end infinite; /* Typing animation */
|
||||
}
|
||||
|
||||
@keyframes typing {
|
||||
from {
|
||||
width: 0;
|
||||
}
|
||||
to {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink-caret {
|
||||
from, to {
|
||||
border-color: transparent; /* Blinking cursor effect */
|
||||
}
|
||||
50% {
|
||||
border-color: #000;
|
||||
}
|
||||
}
|
||||
155
src/components/Gemini.js
Normal file
155
src/components/Gemini.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { query, collection, onSnapshot, addDoc, where, getDocs, orderBy } from 'firebase/firestore';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
|
||||
import './Gemini.css'; // Make sure to have your CSS styles for the typewriter effect
|
||||
import db from '../firebase';
|
||||
|
||||
function Gemini() {
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [isTyping, setIsTyping] = useState(false);
|
||||
|
||||
const scrollRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const loadChatHistory = async () => {
|
||||
const uid = 'uid'; // Replace with your user ID
|
||||
const discussionId = 'discussionId'; // Replace with your discussion ID
|
||||
|
||||
const collectionPath = `users/${uid}/discussions/${discussionId}/messages`;
|
||||
|
||||
const q = query(collection(db, collectionPath), orderBy('createTime'));
|
||||
|
||||
try {
|
||||
const querySnapshot = await getDocs(q);
|
||||
const loadedMessages = [];
|
||||
|
||||
querySnapshot.forEach((doc) => {
|
||||
const data = doc.data();
|
||||
loadedMessages.push({ text: data.prompt, sender: 'user' });
|
||||
|
||||
if (data.response) {
|
||||
// Simulate delay for typewriter effect
|
||||
setTimeout(() => {
|
||||
// Update the state with the Gemini's response
|
||||
setMessages((prevMessages) => [
|
||||
...prevMessages,
|
||||
{ text: data.response, sender: 'gemini' },
|
||||
]);
|
||||
|
||||
if (scrollRef.current) {
|
||||
// alert(scrollRef.current.scrollHeight)
|
||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||
}
|
||||
}, 1000); // You can adjust the delay as needed
|
||||
}
|
||||
});
|
||||
|
||||
// Sort the messages by createTime before updating the state
|
||||
// loadedMessages.sort((a, b) => a.createTime - b.createTime);
|
||||
setMessages(loadedMessages);
|
||||
|
||||
// Scroll to the bottom
|
||||
if (scrollRef.current) {
|
||||
// alert(scrollRef.current.scrollHeight)
|
||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading chat history:', error);
|
||||
}
|
||||
};
|
||||
|
||||
loadChatHistory();
|
||||
}, []); // Empty dependency array ensures the effect runs only once on component mount
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
if (newMessage.trim() === '') return;
|
||||
|
||||
const uid = 'uid'; // Replace with your user ID
|
||||
const discussionId = 'discussionId'; // Replace with your discussion ID
|
||||
|
||||
const collectionPath = `users/${uid}/discussions/${discussionId}/messages`;
|
||||
const collectionRef = collection(db, collectionPath);
|
||||
|
||||
// Add user's message to the local state first
|
||||
setMessages([...messages, { text: newMessage, sender: 'user' }]);
|
||||
setNewMessage('');
|
||||
|
||||
// Then, add the user's message to the Firestore collection
|
||||
// Your existing code for adding user's message remains unchanged
|
||||
|
||||
const docRef = await addDoc(collectionRef, {
|
||||
prompt: newMessage,
|
||||
});
|
||||
|
||||
// Listen for changes in the Firestore document
|
||||
const unsub = onSnapshot(docRef, (doc) => {
|
||||
const data = doc.data();
|
||||
if (data && data.response) {
|
||||
// Start typewriter animation
|
||||
// setIsTyping(true);
|
||||
|
||||
// Simulate delay for typewriter effect
|
||||
setTimeout(() => {
|
||||
// Update the state with the Gemini's response
|
||||
setMessages((prevMessages) => [...prevMessages, { text: data.response, sender: 'gemini' }]);
|
||||
// Stop typewriter animation
|
||||
// setIsTyping(false);
|
||||
|
||||
// Scroll to the bottom
|
||||
if (scrollRef.current) {
|
||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||
}
|
||||
}, 1000); // You can adjust the delay as needed
|
||||
}
|
||||
});
|
||||
|
||||
if (scrollRef.current) {
|
||||
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ height: '500px', overflowY: 'scroll', border: '1px solid #ccc' }} ref={scrollRef}>
|
||||
{messages.map((message, index) => (
|
||||
<div key={index} className={message.sender === 'user' ? 'user-message' : 'other-message'}>
|
||||
{message.sender === 'gemini' && isTyping ? (
|
||||
// Display typing animation for Gemini's response
|
||||
<div className="typing-animation">Gemini is typing...</div>
|
||||
) : (
|
||||
// Display the actual message
|
||||
<ReactMarkdown>{message.text}</ReactMarkdown>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="user-chat-input">
|
||||
<input
|
||||
type="text"
|
||||
value={newMessage}
|
||||
onChange={(e) => setNewMessage(e.target.value)}
|
||||
placeholder="Type your message..."
|
||||
style={{ marginRight: '8px', padding: '8px', fontSize: '16px', width: '50vw' }}
|
||||
/>
|
||||
<button
|
||||
onClick={handleSendMessage}
|
||||
style={{
|
||||
backgroundColor: '#4CAF50',
|
||||
color: 'white',
|
||||
padding: '10px 20px',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Gemini;
|
||||
Reference in New Issue
Block a user