|
"use client"; |
|
|
|
import { useState, useRef } from "react"; |
|
import { useRouter } from "next/navigation"; |
|
import { Button } from "@/components/ui/button"; |
|
import { Input } from "@/components/ui/input"; |
|
import { ArrowLeft, Mic, MicOff } from "lucide-react"; |
|
import Link from "next/link"; |
|
import { toast } from "@/hooks/use-toast"; |
|
import "../styles/background-pattern.css"; |
|
|
|
export default function CreateGroup() { |
|
const router = useRouter(); |
|
const [groupName, setGroupName] = useState(""); |
|
const [userName, setUserName] = useState(""); |
|
const [isRecording, setIsRecording] = useState(false); |
|
const [audioBlob, setAudioBlob] = useState<Blob | null>(null); |
|
const [isLoading, setIsLoading] = useState(false); |
|
const mediaRecorder = useRef<MediaRecorder | null>(null); |
|
const audioChunks = useRef<Blob[]>([]); |
|
|
|
const startRecording = async () => { |
|
try { |
|
console.log("Requesting microphone access..."); |
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); |
|
console.log("Microphone access granted"); |
|
|
|
mediaRecorder.current = new MediaRecorder(stream); |
|
audioChunks.current = []; |
|
|
|
mediaRecorder.current.ondataavailable = (event) => { |
|
console.log("Audio data received"); |
|
audioChunks.current.push(event.data); |
|
}; |
|
|
|
mediaRecorder.current.onstop = () => { |
|
console.log("Recording finished, creating blob"); |
|
const audioBlob = new Blob(audioChunks.current, { type: "audio/wav" }); |
|
setAudioBlob(audioBlob); |
|
console.log("Audio blob created:", audioBlob.size, "bytes"); |
|
}; |
|
|
|
mediaRecorder.current.start(); |
|
setIsRecording(true); |
|
console.log("Recording started"); |
|
|
|
toast({ |
|
title: "Recording Started", |
|
description: "Speak into your microphone...", |
|
}); |
|
} catch (error) { |
|
console.error("Error accessing microphone:", error); |
|
toast({ |
|
title: "Error", |
|
description: "Could not access microphone. Check your permissions.", |
|
variant: "destructive", |
|
}); |
|
} |
|
}; |
|
|
|
const stopRecording = () => { |
|
if (mediaRecorder.current && isRecording) { |
|
console.log("Stopping recording..."); |
|
mediaRecorder.current.stop(); |
|
setIsRecording(false); |
|
mediaRecorder.current.stream.getTracks().forEach((track) => track.stop()); |
|
toast({ |
|
title: "Recording Complete", |
|
description: "Your voice has been recorded successfully.", |
|
}); |
|
} |
|
}; |
|
|
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { |
|
e.preventDefault(); |
|
if (!groupName.trim() || !userName.trim() || !audioBlob) { |
|
toast({ |
|
title: "Error", |
|
description: "Please fill in all fields and record your voice", |
|
variant: "destructive", |
|
}); |
|
return; |
|
} |
|
|
|
setIsLoading(true); |
|
|
|
try { |
|
|
|
const formData = new FormData(); |
|
formData.append("audio", audioBlob); |
|
formData.append("name", userName); |
|
|
|
const voiceResponse = await fetch("/api/voice", { |
|
method: "POST", |
|
body: formData, |
|
}); |
|
|
|
if (!voiceResponse.ok) throw new Error("Error uploading voice"); |
|
const userData = await voiceResponse.json(); |
|
|
|
|
|
const expirationDate = new Date(); |
|
expirationDate.setDate(expirationDate.getDate() + 30); |
|
document.cookie = `userId=${ |
|
userData.id |
|
};expires=${expirationDate.toUTCString()};path=/`; |
|
document.cookie = `userName=${encodeURIComponent( |
|
userName |
|
)};expires=${expirationDate.toUTCString()};path=/`; |
|
|
|
|
|
console.log("Creating group with:", { |
|
name: groupName, |
|
userId: userData.id, |
|
}); |
|
|
|
try { |
|
const groupResponse = await fetch("/api/group", { |
|
method: "POST", |
|
headers: { |
|
"Content-Type": "application/json", |
|
}, |
|
body: JSON.stringify({ |
|
name: groupName, |
|
userId: userData.id, |
|
}), |
|
}); |
|
|
|
console.log("Response status:", groupResponse.status); |
|
const responseText = await groupResponse.text(); |
|
console.log("Raw response:", responseText); |
|
|
|
let groupData; |
|
try { |
|
groupData = JSON.parse(responseText); |
|
console.log("Parsed response:", groupData); |
|
} catch (e) { |
|
console.error("JSON parsing error:", e); |
|
throw new Error("Invalid server response"); |
|
} |
|
|
|
if (!groupResponse.ok) { |
|
throw new Error(groupData.error || "Error creating group"); |
|
} |
|
|
|
toast({ |
|
title: "Success", |
|
description: "Your game has been created successfully!", |
|
}); |
|
|
|
|
|
router.push(`/group/${groupData.inviteCode}`); |
|
} catch (error) { |
|
console.error("Complete error:", error); |
|
toast({ |
|
title: "Error", |
|
description: "An error occurred while creating the group", |
|
variant: "destructive", |
|
}); |
|
} |
|
} catch (error) { |
|
console.error("Error:", error); |
|
toast({ |
|
title: "Error", |
|
description: "An error occurred while creating the group", |
|
variant: "destructive", |
|
}); |
|
} finally { |
|
setIsLoading(false); |
|
} |
|
}; |
|
|
|
return ( |
|
<div className="min-h-screen relative overflow-hidden"> |
|
<div className="background-pattern" /> |
|
<div className="background-overlay" /> |
|
<div className="background-vignette" /> |
|
|
|
<div className="relative z-10 p-4"> |
|
<div className="max-w-md mx-auto bg-gray-100 bg-opacity-90 backdrop-blur-sm rounded-lg shadow-xl p-8"> |
|
<div className="flex justify-between items-center mb-8"> |
|
<Link href="/"> |
|
<Button |
|
variant="outline" |
|
className="border-orange-400 text-orange-600 hover:bg-orange-100" |
|
> |
|
<ArrowLeft className="mr-2 h-4 w-4" /> |
|
Back |
|
</Button> |
|
</Link> |
|
<h1 className="text-3xl font-grobold font-normal text-center text-orange-600"> |
|
Create Game |
|
</h1> |
|
</div> |
|
|
|
<form onSubmit={handleSubmit} className="space-y-6"> |
|
<div className="space-y-2"> |
|
<label className="text-lg font-grobold font-normal text-orange-600"> |
|
Game Name |
|
</label> |
|
<Input |
|
type="text" |
|
value={groupName} |
|
onChange={(e) => setGroupName(e.target.value)} |
|
className="bg-orange-50 text-orange-800 placeholder-orange-300 border-orange-200 focus:border-orange-400 focus:ring-orange-400" |
|
placeholder="Enter game name" |
|
required |
|
/> |
|
</div> |
|
|
|
<div className="space-y-2"> |
|
<label className="text-lg font-grobold font-normal text-orange-600"> |
|
Your Username |
|
</label> |
|
<Input |
|
type="text" |
|
value={userName} |
|
onChange={(e) => setUserName(e.target.value)} |
|
className="bg-orange-50 text-orange-800 placeholder-orange-300 border-orange-200 focus:border-orange-400 focus:ring-orange-400" |
|
placeholder="Enter your username" |
|
required |
|
/> |
|
</div> |
|
|
|
<div className="space-y-2"> |
|
<label className="text-lg font-grobold font-normal text-orange-600"> |
|
Your Voice |
|
</label> |
|
<div className="flex items-center space-x-4"> |
|
<Button |
|
type="button" |
|
onClick={isRecording ? stopRecording : startRecording} |
|
className={`flex-1 py-6 ${ |
|
isRecording |
|
? "bg-red-500 hover:bg-red-600" |
|
: "bg-orange-500 hover:bg-orange-600" |
|
} text-white`} |
|
> |
|
{isRecording ? ( |
|
<> |
|
<MicOff className="mr-2 h-5 w-5" /> |
|
Stop |
|
</> |
|
) : ( |
|
<> |
|
<Mic className="mr-2 h-5 w-5" /> |
|
Record |
|
</> |
|
)} |
|
</Button> |
|
</div> |
|
{audioBlob && !isRecording && ( |
|
<div className="text-green-600 font-medium text-center mt-2"> |
|
✓ Voice recorded |
|
</div> |
|
)} |
|
</div> |
|
|
|
<Button |
|
type="submit" |
|
disabled={isLoading || !audioBlob} |
|
className={`w-full py-6 text-lg font-grobold font-normal ${ |
|
isLoading || !audioBlob |
|
? "bg-gray-400" |
|
: "bg-green-500 hover:bg-green-600" |
|
} text-white`} |
|
> |
|
{isLoading ? "Creating..." : "Create Game"} |
|
</Button> |
|
</form> |
|
</div> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|