Reubencf commited on
Commit
453beea
·
1 Parent(s): eb54b0f

fixing some things before video

Browse files
Files changed (1) hide show
  1. app/components/VoiceApp.tsx +102 -36
app/components/VoiceApp.tsx CHANGED
@@ -8,6 +8,7 @@ import {
8
  BookOpen,
9
  Play,
10
  Stop,
 
11
  DownloadSimple,
12
  ArrowClockwise,
13
  SpinnerGap
@@ -37,6 +38,9 @@ export function VoiceApp({ onClose, onMinimize, onMaximize, onFocus, zIndex }: V
37
  const [voiceContents, setVoiceContents] = useState<VoiceContent[]>([])
38
  const [currentlyPlaying, setCurrentlyPlaying] = useState<string | null>(null)
39
  const [audioElement, setAudioElement] = useState<HTMLAudioElement | null>(null)
 
 
 
40
 
41
  // Load saved content from server and localStorage
42
  useEffect(() => {
@@ -84,27 +88,58 @@ export function VoiceApp({ onClose, onMinimize, onMaximize, onFocus, zIndex }: V
84
  await loadContent()
85
  }
86
 
 
 
 
 
 
 
87
  const handlePlay = (content: VoiceContent) => {
88
  if (!content.audioUrl) return
89
 
90
- // Stop current audio if playing
 
 
 
 
 
 
 
 
 
 
 
91
  if (audioElement) {
92
  audioElement.pause()
93
  audioElement.currentTime = 0
94
  }
95
 
96
- if (currentlyPlaying === content.id) {
97
- setCurrentlyPlaying(null)
98
- setAudioElement(null)
99
- } else {
100
- const audio = new Audio(content.audioUrl)
101
- audio.onended = () => {
102
- setCurrentlyPlaying(null)
103
- setAudioElement(null)
104
- }
105
- audio.play()
106
- setAudioElement(audio)
107
- setCurrentlyPlaying(content.id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  }
109
  }
110
 
@@ -114,18 +149,28 @@ export function VoiceApp({ onClose, onMinimize, onMaximize, onFocus, zIndex }: V
114
  audioElement.currentTime = 0
115
  setAudioElement(null)
116
  setCurrentlyPlaying(null)
 
 
117
  }
118
  }
119
 
120
- const handleDownload = (content: VoiceContent) => {
121
  if (!content.audioUrl) return
122
 
123
- const link = document.createElement('a')
124
- link.href = content.audioUrl
125
- link.download = `${content.title.replace(/\s+/g, '_')}.mp3`
126
- document.body.appendChild(link)
127
- link.click()
128
- document.body.removeChild(link)
 
 
 
 
 
 
 
 
129
  }
130
 
131
  const handleRefresh = () => {
@@ -206,8 +251,8 @@ export function VoiceApp({ onClose, onMinimize, onMaximize, onFocus, zIndex }: V
206
  <div className="flex items-start justify-between mb-3">
207
  <div className="flex items-center gap-3">
208
  <div className={`w-10 h-10 rounded-lg flex items-center justify-center ${content.type === 'song'
209
- ? 'bg-purple-100 text-purple-600'
210
- : 'bg-blue-100 text-blue-600'
211
  }`}>
212
  {content.type === 'song' ? (
213
  <MusicNote size={20} weight="fill" />
@@ -258,25 +303,46 @@ export function VoiceApp({ onClose, onMinimize, onMaximize, onFocus, zIndex }: V
258
  )}
259
 
260
  {content.audioUrl && (
261
- <button
262
- onClick={() => handlePlay(content)}
263
- className={`w-full flex items-center justify-center gap-2 py-2.5 rounded-lg font-medium text-sm transition-all ${currentlyPlaying === content.id
264
- ? 'bg-red-50 text-red-600 border border-red-100 hover:bg-red-100'
265
- : 'bg-[#F5F5F7] text-gray-700 border border-gray-200 hover:bg-gray-200 hover:border-gray-300'
266
- }`}
267
- >
268
  {currentlyPlaying === content.id ? (
269
- <>
270
- <Stop size={16} weight="fill" />
271
- Stop Playback
272
- </>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  ) : (
274
- <>
 
 
 
275
  <Play size={16} weight="fill" />
276
  Play Audio
277
- </>
278
  )}
279
- </button>
280
  )}
281
  </div>
282
  )}
 
8
  BookOpen,
9
  Play,
10
  Stop,
11
+ Pause,
12
  DownloadSimple,
13
  ArrowClockwise,
14
  SpinnerGap
 
38
  const [voiceContents, setVoiceContents] = useState<VoiceContent[]>([])
39
  const [currentlyPlaying, setCurrentlyPlaying] = useState<string | null>(null)
40
  const [audioElement, setAudioElement] = useState<HTMLAudioElement | null>(null)
41
+ const [isPlaying, setIsPlaying] = useState(false)
42
+ const [currentTime, setCurrentTime] = useState(0)
43
+ const [duration, setDuration] = useState(0)
44
 
45
  // Load saved content from server and localStorage
46
  useEffect(() => {
 
88
  await loadContent()
89
  }
90
 
91
+ const formatTime = (time: number) => {
92
+ const minutes = Math.floor(time / 60)
93
+ const seconds = Math.floor(time % 60)
94
+ return `${minutes}:${seconds.toString().padStart(2, '0')}`
95
+ }
96
+
97
  const handlePlay = (content: VoiceContent) => {
98
  if (!content.audioUrl) return
99
 
100
+ if (currentlyPlaying === content.id && audioElement) {
101
+ if (isPlaying) {
102
+ audioElement.pause()
103
+ setIsPlaying(false)
104
+ } else {
105
+ audioElement.play()
106
+ setIsPlaying(true)
107
+ }
108
+ return
109
+ }
110
+
111
+ // Stop previous
112
  if (audioElement) {
113
  audioElement.pause()
114
  audioElement.currentTime = 0
115
  }
116
 
117
+ const audio = new Audio(content.audioUrl)
118
+
119
+ audio.addEventListener('loadedmetadata', () => {
120
+ setDuration(audio.duration)
121
+ })
122
+
123
+ audio.addEventListener('timeupdate', () => {
124
+ setCurrentTime(audio.currentTime)
125
+ })
126
+
127
+ audio.addEventListener('ended', () => {
128
+ setIsPlaying(false)
129
+ setCurrentTime(0)
130
+ })
131
+
132
+ audio.play()
133
+ setAudioElement(audio)
134
+ setCurrentlyPlaying(content.id)
135
+ setIsPlaying(true)
136
+ }
137
+
138
+ const handleSeek = (e: React.ChangeEvent<HTMLInputElement>) => {
139
+ const time = parseFloat(e.target.value)
140
+ setCurrentTime(time)
141
+ if (audioElement) {
142
+ audioElement.currentTime = time
143
  }
144
  }
145
 
 
149
  audioElement.currentTime = 0
150
  setAudioElement(null)
151
  setCurrentlyPlaying(null)
152
+ setIsPlaying(false)
153
+ setCurrentTime(0)
154
  }
155
  }
156
 
157
+ const handleDownload = async (content: VoiceContent) => {
158
  if (!content.audioUrl) return
159
 
160
+ try {
161
+ const response = await fetch(content.audioUrl)
162
+ const blob = await response.blob()
163
+ const url = window.URL.createObjectURL(blob)
164
+ const link = document.createElement('a')
165
+ link.href = url
166
+ link.download = `${content.title.replace(/\s+/g, '_')}.mp3`
167
+ document.body.appendChild(link)
168
+ link.click()
169
+ document.body.removeChild(link)
170
+ window.URL.revokeObjectURL(url)
171
+ } catch (error) {
172
+ console.error('Download failed:', error)
173
+ }
174
  }
175
 
176
  const handleRefresh = () => {
 
251
  <div className="flex items-start justify-between mb-3">
252
  <div className="flex items-center gap-3">
253
  <div className={`w-10 h-10 rounded-lg flex items-center justify-center ${content.type === 'song'
254
+ ? 'bg-purple-100 text-purple-600'
255
+ : 'bg-blue-100 text-blue-600'
256
  }`}>
257
  {content.type === 'song' ? (
258
  <MusicNote size={20} weight="fill" />
 
303
  )}
304
 
305
  {content.audioUrl && (
306
+ <div className="mt-3">
 
 
 
 
 
 
307
  {currentlyPlaying === content.id ? (
308
+ <div className="bg-white rounded-lg border border-gray-200 p-3 space-y-2">
309
+ <div className="flex items-center gap-3">
310
+ <button
311
+ onClick={() => handlePlay(content)}
312
+ className="w-8 h-8 flex items-center justify-center rounded-full bg-gray-900 text-white hover:bg-gray-800 transition-colors"
313
+ >
314
+ {isPlaying ? (
315
+ <Pause size={14} weight="fill" />
316
+ ) : (
317
+ <Play size={14} weight="fill" />
318
+ )}
319
+ </button>
320
+ <div className="flex-1">
321
+ <input
322
+ type="range"
323
+ min="0"
324
+ max={duration || 100}
325
+ value={currentTime}
326
+ onChange={handleSeek}
327
+ className="w-full h-1 bg-gray-200 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:bg-gray-900 [&::-webkit-slider-thumb]:rounded-full"
328
+ />
329
+ <div className="flex justify-between text-[10px] text-gray-500 mt-1 font-medium">
330
+ <span>{formatTime(currentTime)}</span>
331
+ <span>{formatTime(duration)}</span>
332
+ </div>
333
+ </div>
334
+ </div>
335
+ </div>
336
  ) : (
337
+ <button
338
+ onClick={() => handlePlay(content)}
339
+ className="w-full flex items-center justify-center gap-2 py-2.5 rounded-lg font-medium text-sm bg-[#F5F5F7] text-gray-700 border border-gray-200 hover:bg-gray-200 hover:border-gray-300 transition-all"
340
+ >
341
  <Play size={16} weight="fill" />
342
  Play Audio
343
+ </button>
344
  )}
345
+ </div>
346
  )}
347
  </div>
348
  )}