diff --git a/client/src/pages/Home/Home.jsx b/client/src/pages/Home/Home.jsx index 6ac15ec..62eeb46 100644 --- a/client/src/pages/Home/Home.jsx +++ b/client/src/pages/Home/Home.jsx @@ -1,13 +1,164 @@ import "./styles.sass"; import {StateContext} from "@/common/contexts/StateContext"; -import {useContext} from "react"; +import {useContext, useState, useEffect, useRef} from "react"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faMusic } from '@fortawesome/free-solid-svg-icons'; + +const songs = ["Twinkle Twinkle Little Star", "Happy Birthday", "Jingle Bells", "Mary Had a Little Lamb", "Old MacDonald Had a Farm"]; export const Home = () => { const {setCurrentState} = useContext(StateContext); + const [frequency, setFrequency] = useState(440); + const [isPlaying, setIsPlaying] = useState(false); + const [currentSong, setCurrentSong] = useState(songs[Math.floor(Math.random() * songs.length)]); + const [dragging, setDragging] = useState(false); + const audioContextRef = useRef(null); + const oscillatorRef = useRef(null); + const gainNodeRef = useRef(null); + const sliderRef = useRef(null); + const [messages, setMessages] = useState([]); + const [inputValue, setInputValue] = useState(""); + const messageEndRef = useRef(null); + + const startAudio = () => { + if (!audioContextRef.current) { + audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)(); + } + if (!oscillatorRef.current) { + oscillatorRef.current = audioContextRef.current.createOscillator(); + oscillatorRef.current.type = 'sine'; + oscillatorRef.current.frequency.setValueAtTime(frequency, audioContextRef.current.currentTime); + + gainNodeRef.current = audioContextRef.current.createGain(); + gainNodeRef.current.gain.setValueAtTime(0.5, audioContextRef.current.currentTime); + + oscillatorRef.current.connect(gainNodeRef.current); + gainNodeRef.current.connect(audioContextRef.current.destination); + oscillatorRef.current.start(); + } else { + oscillatorRef.current.frequency.setValueAtTime(frequency, audioContextRef.current.currentTime); + } + setIsPlaying(true); + }; + + const stopAudio = () => { + if (oscillatorRef.current) { + oscillatorRef.current.stop(); + oscillatorRef.current.disconnect(); + oscillatorRef.current = null; + } + setIsPlaying(false); + }; + + const handleMouseDown = (e) => { + setDragging(true); + startAudio(); + handleFrequencyChange(e); + }; + + const handleMouseUp = () => { + setDragging(false); + stopAudio(); + }; + + const handleFrequencyChange = (e) => { + if (isPlaying || dragging) { + const rect = sliderRef.current.getBoundingClientRect(); + const x = e.clientX - rect.left; // x position within the element. + const newFrequency = 20 + (x / rect.width) * 1980; // Scale to frequency range + setFrequency(newFrequency); + } + }; + + useEffect(() => { + if (isPlaying) { + if (oscillatorRef.current) { + oscillatorRef.current.frequency.setValueAtTime(frequency, audioContextRef.current.currentTime); + } + } + }, [frequency, isPlaying]); + + useEffect(() => { + messageEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + const handleSendMessage = () => { + if (inputValue.trim()) { + setMessages([...messages, { sender: "User", text: inputValue }]); + setInputValue(""); + } + }; return (
-

ToneGuessr

+
+ + + +
+
+
+

Play this song:

+
+ Song +
+
{currentSong}
+
A beautiful classic song for you to play on the Otamatone.
+
+
+
+
+
+
+
Chat
+
+
+ {messages.map((message, index) => ( +
+ {message.sender}: + {message.text} +
+ ))} +
+
+
+ setInputValue(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && handleSendMessage()} + placeholder="Type your message..." + /> + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
); -} +} \ No newline at end of file diff --git a/client/src/pages/Home/styles.sass b/client/src/pages/Home/styles.sass index 5eaa76f..3c42f9e 100644 --- a/client/src/pages/Home/styles.sass +++ b/client/src/pages/Home/styles.sass @@ -1,8 +1,257 @@ .home-page display: flex + flex-direction: column align-items: center justify-content: center height: 100vh + background: linear-gradient(135deg, #0f2027, #203a43, #2c5364) + padding: 20px + user-select: none + overflow: hidden + position: relative - h2 - font-size: 48pt \ No newline at end of file + .background-elements + position: absolute + top: 0 + left: 0 + width: 100% + height: 100% + overflow: hidden + z-index: 0 + + .music-note + position: absolute + font-size: 24pt + color: rgba(255, 255, 255, 0.5) + animation: float-notes 5s infinite ease-in-out + + &.note-1 + top: 20% + left: 30% + + &.note-2 + top: 60% + left: 80% + + &.note-3 + top: 90% + left: 50% + + @keyframes float-notes + 0% + transform: translateY(0) + 50% + transform: translateY(-10px) + 100% + transform: translateY(0) + + .main-content + display: flex + flex-direction: row + align-items: flex-start + justify-content: center + width: 100% + padding: 20px + z-index: 1 + + .song-display + display: flex + flex-direction: column + align-items: center + justify-content: center + width: 50% + margin-right: 20px + color: #fff + text-align: center + + h2 + font-size: 36pt + color: #fff + margin-bottom: 20px + + .song-card + display: flex + flex-direction: row + align-items: center + background-color: rgba(255, 255, 255, 0.1) + padding: 20px + border-radius: 20px + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5) + backdrop-filter: blur(10px) + border: 1px solid rgba(255, 255, 255, 0.2) + max-width: 500px + + img + width: 100px + height: 100px + border-radius: 10px + margin-right: 20px + + .song-info + display: flex + flex-direction: column + align-items: flex-start + + .song-name + font-size: 24pt + color: #fff + margin-bottom: 10px + + .song-description + font-size: 14pt + color: #aaa + + .chat-window + width: 50% + height: 400px + background: rgba(255, 255, 255, 0.1) + backdrop-filter: blur(10px) + border-radius: 10px + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + border: 1px solid rgba(255, 255, 255, 0.2) + display: flex + flex-direction: column + overflow: hidden + + .chat-header + display: flex + align-items: center + padding: 10px + background: rgba(30, 30, 30, 0.5) + border-bottom: 1px solid rgba(255, 255, 255, 0.2) + + .account-icon + width: 30px + height: 30px + background: url('https://place-hold.it/100x100') no-repeat center + background-size: cover + border-radius: 50% + margin-right: 10px + + .chat-title + font-size: 16pt + color: #fff + + .chat-messages + flex: 1 + padding: 10px + overflow-y: auto + color: #fff + + .message + margin-bottom: 10px + + .message-sender + font-weight: bold + + .message-text + margin-left: 10px + + .chat-input + display: flex + padding: 10px + background: rgba(30, 30, 30, 0.5) + border-top: 1px solid rgba(255, 255, 255, 0.2) + + input + flex: 1 + padding: 10px + border: none + border-radius: 5px + outline: none + background: rgba(255, 255, 255, 0.2) + color: #fff + + button + margin-left: 10px + padding: 10px + background-color: #203a43 + color: #fff + border: none + border-radius: 5px + cursor: pointer + + &:hover + background-color: #1e1e1e + + .otamatone-container + display: flex + justify-content: center + align-items: center + position: fixed + left: 0 + right: 0 + bottom: 0 + padding: 20px + background-color: rgba(30, 30, 30, 0.5) + backdrop-filter: blur(10px) + border-radius: 20px + margin: 20px + z-index: 1 + + .otamatone + display: flex + flex-direction: row + align-items: center + cursor: pointer + padding: 10px + width: 100% + + .otamatone-neck + flex: 1 + height: 20px + background: linear-gradient(135deg, #000, #444) + border-radius: 10px + position: relative + + .frequency-indicator + position: absolute + top: -10px + width: 20px + height: 20px + background-color: #ff0000 + border-radius: 50% + transform: translateX(-50%) + + .note-marker + position: absolute + top: -30px + font-size: 24pt + color: #fff + + .otamatone-face + width: 100px + height: 100px + background: radial-gradient(circle, #fff, #ddd) + border-radius: 50% + display: flex + flex-direction: column + align-items: center + justify-content: center + position: relative + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) + + .otamatone-eyes + display: flex + justify-content: space-between + width: 60% + position: absolute + top: 30px + + .eye + width: 15px + height: 15px + background-color: #000 + border-radius: 50% + + .left-eye + margin-right: 10px + + .right-eye + margin-left: 10px + + .otamatone-mouth + background-color: #000 + border-radius: 50% + position: absolute + bottom: 10px \ No newline at end of file