gonzmart's picture
alfa version
979db68
import { useState, useEffect, useRef } from 'react';
import messageDatabase from './data.json';
import './App.css';
export default function ChatDisplay() {
const [isActive, setIsActive] = useState(false);
const [showModal, setShowModal] = useState(true);
const [currentMessageIndex, setCurrentMessageIndex] = useState(
Math.floor(Math.random() * messageDatabase.length)
);
const [showFeedback, setShowFeedback] = useState(false);
const [message, setMessage] = useState(() => messageDatabase[currentMessageIndex].question);
const [messagesSent, setMessagesSent] = useState(false);
const [sentMessage, setSentMessage] = useState('');
const [isThinking, setIsThinking] = useState(false);
const [userGuessCorrect, setUserGuessCorrect] = useState(false);
const [showAnswer, setShowAnswer] = useState(false);
const [showTopAnimation, setShowTopAnimation] = useState(false);
const [animatedMessages, setAnimatedMessages] = useState({
userMessage: false,
thinking: false,
feedback: false
});
const [reasoningButtonActive, setReasoningButtonActive] = useState(false);
const [searchButtonActive, setSearchButtonActive] = useState(false);
const getRandomMessageIndex = () => {
let newIndex;
do {
newIndex = Math.floor(Math.random() * messageDatabase.length);
} while (newIndex === currentMessageIndex && messageDatabase.length > 1);
return newIndex;
};
const loadNewQuestion = () => {
const newIndex = getRandomMessageIndex();
setCurrentMessageIndex(newIndex);
setMessage(messageDatabase[newIndex].question);
setMessagesSent(false);
setShowFeedback(false);
setShowAnswer(false);
setSentMessage('');
};
const isMessageTypeMatching = () => {
const currentType = messageDatabase[currentMessageIndex].type;
// Match reasoning type with reasoning button
if (currentType === "reasoning" && reasoningButtonActive) return true;
// Match search/standard type with search button
if ((currentType === "search" || currentType === "standard") && searchButtonActive) return true;
// Match default/standard type with no button selection
if (currentType === "standard" && !reasoningButtonActive && !searchButtonActive) return true;
return false;
};
const handleUserGuess = () => {
const isCorrect = isMessageTypeMatching();
setUserGuessCorrect(isCorrect);
setShowFeedback(true);
};
const handleSubmit = (e) => {
e.preventDefault();
if (!message.trim()) return;
// When "Send" is clicked (not in Next state)
if (!messagesSent) {
setSentMessage(message);
setMessagesSent(true);
setIsThinking(true);
setMessage(''); // Clear input immediately when Send is clicked
setTimeout(() => {
setIsThinking(false);
handleUserGuess();
}, 1500);
}
};
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit(e);
}
};
useEffect(() => {
if (!messagesSent) {
setAnimatedMessages({
userMessage: false,
thinking: false,
feedback: false
});
}
}, [messagesSent]);
function UserMessage({ text }) {
const shouldAnimate = !animatedMessages.userMessage;
useEffect(() => {
if (shouldAnimate) {
setAnimatedMessages(prev => ({ ...prev, userMessage: true }));
}
}, [shouldAnimate]);
return (
<div className={`message user-message ${shouldAnimate ? 'message-send-animation' : ''}`}>
<p>{text}</p>
</div>
);
}
function FeedbackMessage() {
const currentType = messageDatabase[currentMessageIndex].type;
const shouldBeReasoning = currentType === "reasoning";
const shouldAnimate = !animatedMessages.feedback;
useEffect(() => {
if (shouldAnimate) {
setAnimatedMessages(prev => ({ ...prev, feedback: true }));
}
}, [shouldAnimate]);
if (userGuessCorrect) {
return (
<div className={`message feedback-message success ${shouldAnimate ? 'message-send-animation' : ''}`}>
<p>πŸŽ‰ Congratulations! You correctly identified this as a{shouldBeReasoning ? ' reasoning' : ' standard'} prompt.</p>
</div>
);
} else {
return (
<div className={`message feedback-message error ${shouldAnimate ? 'message-send-animation' : ''}`}>
<p>❌ Incorrect. This is a{shouldBeReasoning ? ' reasoning' : ' standard'} prompt.</p>
<p className="error-explanation">
{shouldBeReasoning
? "This prompt requires detailed explanation and step-by-step reasoning to solve the problem."
: "This prompt requires a direct, concise answer without detailed explanation."}
</p>
</div>
);
}
}
function WelcomeModal() {
return (
showModal && (
<div className="modal-overlay">
<div className="modal">
<h2>🌿 Did you know? </h2>
<p>Every prompt counts! Choosing the right AI model saves energy and protects our planet. 🌎✨</p>
<img src="images/cute_world_mini.png" style={{ width: '55%' }} />
<p>Play smart, pick wisely, and reduce your digital footprint! πŸŒ±πŸ’‘</p>
<button onClick={() => setShowModal(false)}>Start</button>
</div>
</div>
)
);
}
function ChatInput() {
const [isNewPrompt, setIsNewPrompt] = useState(false);
const initialLoadComplete = useRef(false);
// Only run once when component mounts
useEffect(() => {
// Mark initial load as complete after a short delay
const timer = setTimeout(() => {
initialLoadComplete.current = true;
}, 500);
return () => clearTimeout(timer);
}, []);
const handleButtonClick = (button) => {
// Allow toggling between active/inactive states
if (button === 'reasoning') {
// If already active, deselect it
if (reasoningButtonActive) {
setReasoningButtonActive(false);
} else {
// Otherwise, activate it and deactivate the other button
setReasoningButtonActive(true);
setSearchButtonActive(false);
}
} else if (button === 'search') {
// If already active, deselect it
if (searchButtonActive) {
setSearchButtonActive(false);
} else {
// Otherwise, activate it and deactivate the other button
setSearchButtonActive(true);
setReasoningButtonActive(false);
}
}
};
const handleNextClick = () => {
// Only set new prompt flag if initial load is complete
if (initialLoadComplete.current) {
setIsNewPrompt(true);
}
// Show the energy animation
setShowTopAnimation(true);
// Hide animation after a delay
setTimeout(() => {
setShowTopAnimation(false);
}, 3000);
loadNewQuestion();
// Reset the new prompt flag after animation
if (initialLoadComplete.current) {
setTimeout(() => setIsNewPrompt(false), 800);
}
};
// Simplify class name logic - no complex conditions
const getInputClassName = () => {
const classes = ['chat-input'];
// Only apply new-prompt class if initial load is complete
if (initialLoadComplete.current && isNewPrompt) {
classes.push('new-prompt');
}
return classes.join(' ');
};
return (
<div className="chat-input-container">
<form className="chat-input-wrapper" onSubmit={handleSubmit}>
<div className="button-group">
<div className="tooltip-wrapper">
<button
onClick={() => handleButtonClick('reasoning')}
className={`standard-button reasoning-button ${reasoningButtonActive ? 'active' : ''}`}
type="button"
disabled={messagesSent}
>
Reason
</button>
<div className="tooltip">
Use for prompts that require detailed explanations and step-by-step reasoning
</div>
</div>
<div className="tooltip-wrapper">
<button
onClick={() => handleButtonClick('search')}
className={`standard-button search-button ${searchButtonActive ? 'active' : ''}`}
type="button"
disabled={messagesSent}
>
Search
</button>
<div className="tooltip">
Use for prompts that need simple, direct answers without detailed explanation
</div>
</div>
</div>
<div className="input-with-button">
<div className="tooltip-wrapper input-tooltip-wrapper">
<textarea
className={getInputClassName()}
value={showModal ? '' : message}
onKeyPress={handleKeyPress}
placeholder="Click next for next prompt"
rows="3"
readOnly={!messagesSent && message === messageDatabase[currentMessageIndex].question}
/>
<div className="tooltip input-tooltip">
First select a model type above, then press Send to submit your answer
</div>
</div>
<button
type={messagesSent && !showFeedback ? "button" : "submit"}
className="answer-button"
onClick={messagesSent && showFeedback ? handleNextClick : undefined}
disabled={messagesSent && !showFeedback}
>
{messagesSent && showFeedback ? 'Next' : 'Send'}
</button>
</div>
</form>
</div>
);
}
function ThinkingAnimation() {
const shouldAnimate = !animatedMessages.thinking;
useEffect(() => {
if (shouldAnimate) {
setAnimatedMessages(prev => ({ ...prev, thinking: true }));
}
}, [shouldAnimate]);
return (
<div className={`thinking ${shouldAnimate ? 'message-send-animation' : ''}`}>
<div className="dot"></div>
<div className="dot"></div>
<div className="dot"></div>
</div>
);
}
// Updated CompanyLogo component with both logos at the bottom
function CompanyLogo() {
return (
<>
<img
src="images/cotec.png"
alt="Company Logo Right"
className="company-logo company-logo-right"
/>
<img
src="images/upm4.png"
alt="Company Logo Left"
className="company-logo company-logo-left"
/>
</>
);
}
function EnergyAnimation() {
return (
<div className={`energy-animation ${showTopAnimation ? 'show' : ''}`}>
<div className="energy-icon">
{userGuessCorrect ? '🌳' : 'πŸ—‘οΈ'}
</div>
<div className="energy-text">
{userGuessCorrect ? 'Energy saved! Good choice!' : 'Choose wisely next time to save energy!'}
</div>
</div>
);
}
return (
<>
<WelcomeModal />
<EnergyAnimation />
<div className="chat-container">
<div className="messages-container">
{messagesSent && <UserMessage text={`"${sentMessage}"`} />}
{messagesSent && isThinking && <ThinkingAnimation />}
{messagesSent && showFeedback && !isThinking && <FeedbackMessage />}
</div>
<ChatInput />
</div>
<CompanyLogo />
</>
);
}