diff --git a/frontend/src/index.sass b/frontend/src/index.sass index 0cf3a6a..02bee22 100644 --- a/frontend/src/index.sass +++ b/frontend/src/index.sass @@ -8,6 +8,8 @@ $c-blue: rgb(28, 176, 246) $c-blue-shadow: rgb(24, 153, 214) $c-green: rgb(88, 204, 2) $c-green-shadow: rgb(88, 167, 0) +$c-red: rgb(255, 69, 58) +$c-red-shadow: darken($c-red, 10%) $c-white-shadow: rgb(222, 222, 222) $border-radius: 16px $shadow-width: 0 4px 0 @@ -164,6 +166,10 @@ h1 width: calc(100% - 40px) padding: 10px 16px +.record-btn.red + background-color: $c-red + box-shadow: $shadow-width $c-red-shadow + .chat-area display: flex flex-direction: column diff --git a/frontend/src/pages/Character.tsx b/frontend/src/pages/Character.tsx index 70e1655..ee6afab 100644 --- a/frontend/src/pages/Character.tsx +++ b/frontend/src/pages/Character.tsx @@ -1,7 +1,7 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { Icon } from '@iconify/react'; import CharacterBadge from '../components/CharacterBadge'; -import { useState } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { speechToText, characterChatMessage, getAudio } from '../logic/sdk'; export default function Character() { @@ -11,44 +11,53 @@ export default function Character() { type Message = { text: string, sender: string }; const [messages, setMessages] = useState([]); + const [isRecording, setIsRecording] = useState(false); let chunks = [] as any; - let mediaRecorder = null as any; + const mediaRecorder = useRef(null); - navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { - mediaRecorder = new MediaRecorder(stream) - mediaRecorder.ondataavailable = (e: any) => { - chunks.push(e.data) - } - - mediaRecorder.onstop = (e: any) => { - const blob = new Blob(chunks, { type: 'audio/wav' }); - chunks = []; - - const audioFile = new File([blob], "audio.wav", { type: 'audio/wav' }); - - speechToText(audioFile).then((text) => { - setMessages([...messages, { text: text, sender: 'me' }]); - characterChatMessage(sessionId, text).then((response) => { - const { msg, audio_id } = response - getAudio(audio_id).then((audioBlob) => { - const audioFile = new File([audioBlob], "audio.wav", { type: 'audio/wav' }); - const audioUrl = URL.createObjectURL(audioFile); - const audio = new Audio(audioUrl); - audio.play(); - }); - setMessages(prevMessages => [...prevMessages, { text: msg, sender: 'other' }]); - }) - }); - } - }) + useEffect(() => { + navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { + mediaRecorder.current = new MediaRecorder(stream); + mediaRecorder.current.ondataavailable = (e) => { + chunks.push(e.data); + } + + mediaRecorder.current.onstop = (e) => { + setIsRecording(false); + const blob = new Blob(chunks, { type: 'audio/wav' }); + chunks = []; + + const audioFile = new File([blob], "audio.wav", { type: 'audio/wav' }); + + speechToText(audioFile).then((text) => { + setMessages(prevMessages => [...prevMessages, { text: text, sender: 'me' }]); + characterChatMessage(sessionId, text).then((response) => { + const { msg, audio_id } = response + getAudio(audio_id).then((audioBlob) => { + const audioFile = new File([audioBlob], "audio.wav", { type: 'audio/wav' }); + const audioUrl = URL.createObjectURL(audioFile); + const audio = new Audio(audioUrl); + audio.play(); + }); + setMessages(prevMessages => [...prevMessages, { text: msg, sender: 'other' }]); + }) + }); + } + }); + }, []); function handleRecord() { - if (mediaRecorder && mediaRecorder.state === 'inactive') { - mediaRecorder.start(); + if (!isRecording) { + if (mediaRecorder.current) { + mediaRecorder.current.start(); + } + setIsRecording(true); console.log("Recording..."); - } else if (mediaRecorder && mediaRecorder.state === 'recording') { - mediaRecorder.stop(); + } else { + if (mediaRecorder.current) { + mediaRecorder.current.stop(); + } console.log("Stopped recording."); } } @@ -70,7 +79,9 @@ export default function Character() { )) )} - + )