forked from ansh/template-2
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Created a voice recording app with perplexity-like UI
- Loading branch information
Showing
8 changed files
with
302 additions
and
35 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
"use client"; | ||
|
||
import { useState, useEffect } from 'react'; | ||
import { getDocuments } from '../../lib/firebaseUtils'; | ||
import Link from 'next/link'; | ||
import { ArrowLeft } from 'lucide-react'; | ||
|
||
interface Note { | ||
id: string; | ||
text: string; | ||
timestamp: string; | ||
} | ||
|
||
export default function AllNotes() { | ||
const [notes, setNotes] = useState<Note[]>([]); | ||
const [loading, setLoading] = useState(true); | ||
|
||
useEffect(() => { | ||
async function fetchNotes() { | ||
try { | ||
const fetchedNotes = await getDocuments('notes'); | ||
setNotes(fetchedNotes as Note[]); | ||
setLoading(false); | ||
} catch (error) { | ||
console.error('Error fetching notes:', error); | ||
setLoading(false); | ||
} | ||
} | ||
|
||
fetchNotes(); | ||
}, []); | ||
|
||
return ( | ||
<div className="min-h-screen bg-[#1c1c1e] text-white font-sans p-4"> | ||
<Link href="/" className="flex items-center text-orange-500 mb-6"> | ||
<ArrowLeft className="mr-2" /> | ||
Back to Recording | ||
</Link> | ||
<h1 className="text-3xl font-bold mb-6">All Notes</h1> | ||
{loading ? ( | ||
<p>Loading notes...</p> | ||
) : ( | ||
<div className="space-y-4"> | ||
{notes.map((note) => ( | ||
<div key={note.id} className="bg-gray-800 p-4 rounded-lg"> | ||
<p className="text-sm text-gray-400 mb-2"> | ||
{new Date(note.timestamp).toLocaleString()} | ||
</p> | ||
<p>{note.text}</p> | ||
</div> | ||
))} | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,135 @@ | ||
import Link from "next/link"; | ||
"use client"; | ||
|
||
import { useState, useEffect } from 'react'; | ||
import { motion, AnimatePresence } from 'framer-motion'; | ||
import { MicIcon, StopCircleIcon, Loader2 } from 'lucide-react'; | ||
import { useDeepgram } from '../contexts/DeepgramContext'; | ||
import { addDocument } from '../lib/firebaseUtils'; | ||
import { useRouter } from 'next/navigation'; | ||
|
||
export default function Home() { | ||
const [showText, setShowText] = useState(false); | ||
const [lines, setLines] = useState<number[]>(Array(30).fill(0)); | ||
const [currentTime, setCurrentTime] = useState<string>(''); | ||
const [isLoading, setIsLoading] = useState(false); | ||
const { connectToDeepgram, disconnectFromDeepgram, connectionState, realtimeTranscript } = useDeepgram(); | ||
const router = useRouter(); | ||
|
||
const isRecording = connectionState === 1; | ||
|
||
useEffect(() => { | ||
const timer = setInterval(() => { | ||
setCurrentTime(new Date().toLocaleTimeString()); | ||
}, 1000); | ||
|
||
return () => clearInterval(timer); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (isRecording) { | ||
const interval = setInterval(() => { | ||
setLines(prev => prev.map(() => Math.random())); | ||
}, 500); | ||
return () => clearInterval(interval); | ||
} else { | ||
setLines(Array(30).fill(0)); | ||
} | ||
}, [isRecording]); | ||
|
||
const handleRecordToggle = async () => { | ||
if (isRecording) { | ||
disconnectFromDeepgram(); | ||
setShowText(true); | ||
setIsLoading(true); | ||
|
||
if (realtimeTranscript) { | ||
try { | ||
await addDocument('notes', { | ||
text: realtimeTranscript, | ||
timestamp: new Date().toISOString() | ||
}); | ||
console.log('Note saved successfully'); | ||
|
||
// Wait for a short delay to show the loading indicator | ||
await new Promise(resolve => setTimeout(resolve, 1500)); | ||
|
||
// Navigate to the All Notes page | ||
router.push('/all-notes'); | ||
} catch (error) { | ||
console.error('Error saving note:', error); | ||
setIsLoading(false); | ||
} | ||
} | ||
} else { | ||
connectToDeepgram(); | ||
setShowText(false); | ||
} | ||
}; | ||
|
||
return ( | ||
<main className="flex min-h-screen flex-col items-center justify-between p-8"> | ||
<div> | ||
<h2 className="text-2xl font-semibold text-center border p-4 font-mono rounded-md"> | ||
Get started by choosing a template path from the /paths/ folder. | ||
</h2> | ||
</div> | ||
<div> | ||
<h1 className="text-6xl font-bold text-center">Make anything you imagine 🪄</h1> | ||
<h2 className="text-2xl text-center font-light text-gray-500 pt-4"> | ||
This whole page will be replaced when you run your template path. | ||
</h2> | ||
</div> | ||
<div className="w-full grid grid-cols-1 md:grid-cols-3 gap-4"> | ||
<div className="border rounded-lg p-6 hover:bg-gray-100 transition-colors"> | ||
<h3 className="text-xl font-semibold">AI Chat App</h3> | ||
<p className="mt-2 text-sm text-gray-600"> | ||
An intelligent conversational app powered by AI models, featuring real-time responses | ||
and seamless integration with Next.js and various AI providers. | ||
</p> | ||
<div className="flex flex-col h-screen bg-[#1c1c1e] text-white font-sans"> | ||
{/* Status Bar */} | ||
<div className="flex justify-between items-center px-4 py-2 text-sm"> | ||
<span>{currentTime}</span> | ||
<div className="flex space-x-1"> | ||
<div className="w-4 h-4 rounded-full bg-white"></div> | ||
<div className="w-4 h-4 rounded-full bg-white"></div> | ||
<div className="w-4 h-4 rounded-full bg-white"></div> | ||
</div> | ||
<div className="border rounded-lg p-6 hover:bg-gray-100 transition-colors"> | ||
<h3 className="text-xl font-semibold">AI Image Generation App</h3> | ||
<p className="mt-2 text-sm text-gray-600"> | ||
Create images from text prompts using AI, powered by the Replicate API and Next.js. | ||
</p> | ||
</div> | ||
|
||
{/* Main Content */} | ||
<div className="flex-1 flex flex-col justify-between p-4"> | ||
<AnimatePresence> | ||
{(showText || isRecording || isLoading) && ( | ||
<motion.div | ||
initial={{ opacity: 0 }} | ||
animate={{ opacity: 1 }} | ||
exit={{ opacity: 0 }} | ||
className="flex-1 flex items-center justify-center p-4" | ||
> | ||
{isLoading ? ( | ||
<div className="flex flex-col items-center"> | ||
<Loader2 className="w-10 h-10 animate-spin mb-4" /> | ||
<p className="text-lg">Saving your note...</p> | ||
</div> | ||
) : ( | ||
<p className="text-lg text-center"> | ||
{realtimeTranscript || "Start speaking to see real-time transcription."} | ||
</p> | ||
)} | ||
</motion.div> | ||
)} | ||
</AnimatePresence> | ||
|
||
{/* Animated Lines */} | ||
<div className="h-40 flex justify-center items-end space-x-0.5 mb-20"> | ||
{lines.map((height, index) => ( | ||
<motion.div | ||
key={index} | ||
className="w-1 bg-white rounded-full" | ||
initial={{ height: 0 }} | ||
animate={{ height: `${height * 100}%` }} | ||
transition={{ duration: 0.5, ease: "easeInOut" }} | ||
/> | ||
))} | ||
</div> | ||
<div className="border rounded-lg p-6 hover:bg-gray-100 transition-colors"> | ||
<h3 className="text-xl font-semibold">Social Media App</h3> | ||
<p className="mt-2 text-sm text-gray-600"> | ||
A feature-rich social platform with user profiles, posts, and interactions using | ||
Firebase and Next.js. | ||
</p> | ||
|
||
{/* Control Buttons */} | ||
<div className="flex justify-center space-x-4 mb-8"> | ||
<button | ||
onClick={handleRecordToggle} | ||
className="w-20 h-20 rounded-2xl bg-orange-500 hover:bg-orange-600 focus:outline-none flex items-center justify-center" | ||
disabled={isLoading} | ||
> | ||
{isRecording ? ( | ||
<StopCircleIcon size={32} /> | ||
) : ( | ||
<MicIcon size={32} /> | ||
)} | ||
</button> | ||
</div> | ||
</div> | ||
</main> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
'use client'; | ||
|
||
import { useState, useEffect } from 'react'; | ||
import { getDocuments } from '../lib/firebaseUtils'; | ||
|
||
interface Note { | ||
id: string; | ||
text: string; | ||
timestamp: string; | ||
} | ||
|
||
export default function NotesList() { | ||
const [notes, setNotes] = useState<Note[]>([]); | ||
|
||
useEffect(() => { | ||
const fetchNotes = async () => { | ||
const notesData = await getDocuments('notes'); | ||
setNotes(notesData.docs.map(doc => ({ id: doc.id, ...doc.data() } as Note))); | ||
}; | ||
|
||
fetchNotes(); | ||
}, []); | ||
|
||
return ( | ||
<div className="w-full max-w-md mt-8"> | ||
<h2 className="text-2xl font-bold mb-4">Your Notes</h2> | ||
<ul className="space-y-4"> | ||
{notes.map((note) => ( | ||
<li key={note.id} className="bg-white shadow rounded-lg p-4"> | ||
<p className="text-sm text-gray-600">{new Date(note.timestamp).toLocaleString()}</p> | ||
<p className="mt-2">{note.text}</p> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
'use client'; | ||
|
||
import { useState } from 'react'; | ||
import { useDeepgram } from '../contexts/DeepgramContext'; | ||
import { addDocument } from '../lib/firebaseUtils'; | ||
|
||
export default function VoiceRecorder() { | ||
const [isRecording, setIsRecording] = useState(false); | ||
const { connectToDeepgram, disconnectFromDeepgram, connectionState, realtimeTranscript } = useDeepgram(); | ||
|
||
const handleStartRecording = async () => { | ||
await connectToDeepgram(); | ||
setIsRecording(true); | ||
}; | ||
|
||
const handleStopRecording = async () => { | ||
disconnectFromDeepgram(); | ||
setIsRecording(false); | ||
|
||
// Save the note to Firebase | ||
if (realtimeTranscript) { | ||
await addDocument('notes', { | ||
text: realtimeTranscript, | ||
timestamp: new Date().toISOString(), | ||
}); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="w-full max-w-md"> | ||
<button | ||
onClick={isRecording ? handleStopRecording : handleStartRecording} | ||
className={`w-full py-2 px-4 rounded-full ${ | ||
isRecording ? 'bg-red-500 hover:bg-red-600' : 'bg-blue-500 hover:bg-blue-600' | ||
} text-white font-bold`} | ||
> | ||
{isRecording ? 'Stop Recording' : 'Start Recording'} | ||
</button> | ||
{isRecording && ( | ||
<div className="mt-4 p-4 bg-gray-100 rounded-lg"> | ||
<p className="text-sm text-gray-600">{realtimeTranscript}</p> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters