import React, { useState, useEffect, useRef, useCallback } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate, Link, useNavigate } from 'react-router-dom';
import { useReactMediaRecorder } from "react-media-recorder";
import './App.css'; // Your existing App.css
import chatbotLogo from './assets/chatbot.png'; // Import the logo
// Mock user object for demonstration. In your real app, this comes from Firebase.
const mockUser = {
isLoggedIn: true,
};
// =================================================================================
// Chat Components (ChatPage and ChatInput)
// =================================================================================
const ChatInput = ({ onSendMessage, isLoading, setInputText, inputText }) => {
const { status, startRecording, stopRecording, mediaBlobUrl } = useReactMediaRecorder({
audio: true,
mimeType: "audio/webm", // Explicitly set mimetype
});
const [isTranscribing, setIsTranscribing] = useState(false);
const isRecording = status === "recording";
// This effect handles the recorded audio blob
useEffect(() => {
// When a blob URL is available and recording has stopped
if (mediaBlobUrl && status === 'stopped') {
const transcribeAudio = async () => {
setIsTranscribing(true);
try {
// Fetch the audio blob from its URL
const audioBlob = await fetch(mediaBlobUrl).then((res) => res.blob());
// Create a FormData object to send the file
const formData = new FormData();
formData.append("audio_file", audioBlob, "recording.webm");
// Send the audio to the backend transcription endpoint
const response = await fetch("/api/transcribe-audio", {
method: "POST",
body: formData,
});
if (!response.ok) {
throw new Error("Transcription failed");
}
const result = await response.json();
setInputText(result.transcription); // Set the input field with the transcribed text
} catch (error) {
console.error("Error transcribing audio:", error);
alert("Could not transcribe audio. Please try again.");
} finally {
setIsTranscribing(false);
}
};
transcribeAudio();
}
}, [mediaBlobUrl, status, setInputText]);
const handleTextChange = (e) => {
setInputText(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
if (inputText.trim()) {
onSendMessage(inputText);
setInputText('');
}
};
const getPlaceholderText = () => {
if (isRecording) return "Recording...";
if (isTranscribing) return "Transcribing...";
return "Type or hold the mic to talk...";
};
return (
);
};
const ChatPage = () => {
const [chatHistory, setChatHistory] = useState([]);
const [isSending, setIsSending] = useState(false);
const [inputText, setInputText] = useState(""); // State for the input field
const chatEndRef = useRef(null);
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [chatHistory]);
const handleSendMessage = useCallback(async (prompt) => {
if (!prompt.trim() || isSending) return;
const userMessage = { role: 'user', message: prompt };
// Add user message and a temporary placeholder for the bot's response
setChatHistory(prev => [...prev, userMessage, { role: 'assistant', message: '...' }]);
setIsSending(true);
try {
const response = await fetch('/api/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: prompt }),
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullResponse = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const events = chunk.split('\n\n');
for (const event of events) {
if (event.startsWith('data:')) {
const dataStr = event.substring(5).trim();
if (dataStr) {
try {
const dataObj = JSON.parse(dataStr);
if (dataObj.token) {
fullResponse += dataObj.token;
// Update the last message in history with the streaming content
setChatHistory(prev => {
const newHistory = [...prev];
newHistory[newHistory.length - 1] = { role: 'assistant', message: fullResponse + '▌' };
return newHistory;
});
}
} catch (e) {
console.error("Error parsing stream data:", e);
}
}
}
}
}
// Final update to remove the cursor
setChatHistory(prev => {
const newHistory = [...prev];
newHistory[newHistory.length - 1] = { role: 'assistant', message: fullResponse };
return newHistory;
});
} catch (error) {
console.error("Error fetching AI response:", error);
setChatHistory(prev => {
const newHistory = [...prev];
newHistory[newHistory.length - 1] = { role: 'assistant', message: "Sorry, I couldn't get a response. Please try again." };
return newHistory;
});
} finally {
setIsSending(false);
}
}, [isSending]);
return (
{chatHistory.length === 0 ? (
Dobby is here to help!
Start the conversation by typing or using your voice.
) : (
chatHistory.map((chat, index) => (
{chat.role === 'assistant' ? (

) : '👤'}
{chat.message}
))
)}
);
};
// =================================================================================
// Auth Components (Login and SignUp)
// =================================================================================
const AuthForm = ({ isLogin = false }) => {
const navigate = useNavigate();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
setError('');
if (!email || !password) {
setError('Please fill in all fields.');
return;
}
// In a real app, you'd integrate with Firebase or your backend for authentication
// For this mock, we just set isLoggedIn to true
mockUser.isLoggedIn = true;
navigate('/chat');
};
return (
);
};
const Login = () => ;
const SignUp = () => ;
// =================================================================================
// Header and Main App Component
// =================================================================================
const Header = ({ user }) => {
const navigate = useNavigate();
const handleLogout = () => {
mockUser.isLoggedIn = false;
navigate('/login');
};
return (
);
};
function App() {
const [user] = useState(mockUser);
return (
} />
} />
: } />
: } />
);
}
export default App;