feat: implement TTS event handling for creation, completion, and failure in TTSPage and SocketContext
This commit is contained in:
@@ -22,6 +22,7 @@ import { Badge } from '@/components/ui/badge'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Loader2, Mic } from 'lucide-react'
|
||||
import { ttsService, type TTSProvider } from '@/lib/api/services/tts'
|
||||
import { TTS_EVENTS, ttsEvents } from '@/lib/events'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
interface FormData {
|
||||
@@ -88,8 +89,8 @@ export function CreateTTSDialog({ open, onOpenChange }: CreateTTSDialogProps) {
|
||||
onOpenChange(false)
|
||||
handleReset()
|
||||
|
||||
// Trigger refresh of parent list if needed
|
||||
window.dispatchEvent(new CustomEvent('tts-generated'))
|
||||
// Emit event for new TTS created
|
||||
ttsEvents.emit(TTS_EVENTS.TTS_CREATED, response.tts)
|
||||
} catch (error: any) {
|
||||
toast.error(error.response?.data?.detail || 'Failed to generate TTS')
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Calendar, CheckCircle, Loader, Mic, Trash2, Volume2 } from 'lucide-react'
|
||||
import { AlertCircle, Calendar, CheckCircle, Clock, Loader, Mic, Trash2, Volume2, XCircle } from 'lucide-react'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -57,22 +57,36 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) {
|
||||
}
|
||||
|
||||
const getStatusBadge = (tts: TTSResponse) => {
|
||||
const isCompleted = tts.sound_id !== null
|
||||
|
||||
if (isCompleted) {
|
||||
return (
|
||||
<Badge variant="secondary" className="gap-1">
|
||||
<CheckCircle className="h-3 w-3" />
|
||||
Complete
|
||||
</Badge>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<Loader className="h-3 w-3 animate-spin" />
|
||||
Processing
|
||||
</Badge>
|
||||
)
|
||||
switch (tts.status) {
|
||||
case 'completed':
|
||||
return (
|
||||
<Badge variant="secondary" className="gap-1">
|
||||
<CheckCircle className="h-3 w-3" />
|
||||
Completed
|
||||
</Badge>
|
||||
)
|
||||
case 'processing':
|
||||
return (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<Loader className="h-3 w-3 animate-spin" />
|
||||
Processing
|
||||
</Badge>
|
||||
)
|
||||
case 'failed':
|
||||
return (
|
||||
<Badge variant="destructive" className="gap-1">
|
||||
<XCircle className="h-3 w-3" />
|
||||
Failed
|
||||
</Badge>
|
||||
)
|
||||
case 'pending':
|
||||
default:
|
||||
return (
|
||||
<Badge variant="outline" className="gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
Pending
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +97,8 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) {
|
||||
<TableRow>
|
||||
<TableHead>Text</TableHead>
|
||||
<TableHead>Provider</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Options</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
<TableHead className="w-[120px]">Actions</TableHead>
|
||||
</TableRow>
|
||||
@@ -103,9 +117,6 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) {
|
||||
{tts.provider.toUpperCase()}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{getStatusBadge(tts)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{Object.keys(tts.options).length > 0 ? (
|
||||
<div className="flex gap-1 flex-wrap">
|
||||
@@ -119,6 +130,19 @@ export function TTSTable({ ttsHistory, onTTSDeleted }: TTSTableProps) {
|
||||
<span className="text-muted-foreground text-sm">None</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="space-y-1">
|
||||
{getStatusBadge(tts)}
|
||||
{tts.error && (
|
||||
<div
|
||||
className="text-xs text-destructive max-w-48 truncate"
|
||||
title={tts.error}
|
||||
>
|
||||
{tts.error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-1 text-sm text-muted-foreground">
|
||||
<Calendar className="h-3 w-3" />
|
||||
|
||||
@@ -12,11 +12,13 @@ import {
|
||||
EXTRACTION_EVENTS,
|
||||
PLAYER_EVENTS,
|
||||
SOUND_EVENTS,
|
||||
TTS_EVENTS,
|
||||
USER_EVENTS,
|
||||
authEvents,
|
||||
extractionEvents,
|
||||
playerEvents,
|
||||
soundEvents,
|
||||
ttsEvents,
|
||||
userEvents,
|
||||
} from '../lib/events'
|
||||
import { extractionsService } from '../lib/api/services/extractions'
|
||||
@@ -128,10 +130,10 @@ export function SocketProvider({ children }: SocketProviderProps) {
|
||||
// Listen for extraction status updates
|
||||
newSocket.on('extraction_status_update', data => {
|
||||
const { extraction_id, status, title, error } = data
|
||||
|
||||
|
||||
// Emit local event for other components to listen to
|
||||
extractionEvents.emit(EXTRACTION_EVENTS.EXTRACTION_STATUS_UPDATED, data)
|
||||
|
||||
|
||||
// Handle specific status events
|
||||
switch (status) {
|
||||
case 'processing':
|
||||
@@ -158,6 +160,30 @@ export function SocketProvider({ children }: SocketProviderProps) {
|
||||
}
|
||||
})
|
||||
|
||||
// Listen for TTS status updates
|
||||
newSocket.on('tts_completed', data => {
|
||||
const { tts_id, sound_id } = data
|
||||
|
||||
// Emit local event for other components to listen to
|
||||
ttsEvents.emit(TTS_EVENTS.TTS_COMPLETED, data)
|
||||
|
||||
toast.success('TTS generation completed', {
|
||||
duration: 3000,
|
||||
})
|
||||
})
|
||||
|
||||
newSocket.on('tts_failed', data => {
|
||||
const { tts_id, error } = data
|
||||
|
||||
// Emit local event for other components to listen to
|
||||
ttsEvents.emit(TTS_EVENTS.TTS_FAILED, data)
|
||||
|
||||
toast.error('TTS generation failed', {
|
||||
description: error,
|
||||
duration: 5000,
|
||||
})
|
||||
})
|
||||
|
||||
return newSocket
|
||||
}, [user, fetchAndShowOngoingExtractions])
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ export interface TTSResponse {
|
||||
text: string
|
||||
provider: string
|
||||
options: Record<string, any>
|
||||
status: string
|
||||
error: string | null
|
||||
sound_id: number | null
|
||||
user_id: number
|
||||
created_at: string
|
||||
|
||||
@@ -37,6 +37,7 @@ export const playerEvents = new EventEmitter()
|
||||
export const soundEvents = new EventEmitter()
|
||||
export const userEvents = new EventEmitter()
|
||||
export const extractionEvents = new EventEmitter()
|
||||
export const ttsEvents = new EventEmitter()
|
||||
|
||||
// Auth event types
|
||||
export const AUTH_EVENTS = {
|
||||
@@ -69,3 +70,11 @@ export const EXTRACTION_EVENTS = {
|
||||
EXTRACTION_COMPLETED: 'extraction_completed',
|
||||
EXTRACTION_FAILED: 'extraction_failed',
|
||||
} as const
|
||||
|
||||
// TTS event types
|
||||
export const TTS_EVENTS = {
|
||||
TTS_STATUS_UPDATED: 'tts_status_updated',
|
||||
TTS_CREATED: 'tts_created',
|
||||
TTS_COMPLETED: 'tts_completed',
|
||||
TTS_FAILED: 'tts_failed',
|
||||
} as const
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
type TTSSortOrder,
|
||||
ttsService,
|
||||
} from '@/lib/api/services/tts'
|
||||
import { TTS_EVENTS, ttsEvents } from '@/lib/events'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -82,15 +83,30 @@ export function TTSPage() {
|
||||
}
|
||||
}, [debouncedSearchQuery, sortBy, sortOrder, pageSize])
|
||||
|
||||
// Listen for TTS generation events to refresh the list
|
||||
// Listen for TTS events to refresh the list
|
||||
useEffect(() => {
|
||||
const handleTTSGenerated = () => {
|
||||
const handleTTSCompleted = () => {
|
||||
fetchTTSHistory()
|
||||
}
|
||||
|
||||
window.addEventListener('tts-generated', handleTTSGenerated)
|
||||
const handleTTSFailed = () => {
|
||||
fetchTTSHistory()
|
||||
}
|
||||
|
||||
const handleTTSCreated = () => {
|
||||
fetchTTSHistory()
|
||||
}
|
||||
|
||||
// Subscribe to TTS events
|
||||
ttsEvents.on(TTS_EVENTS.TTS_COMPLETED, handleTTSCompleted)
|
||||
ttsEvents.on(TTS_EVENTS.TTS_FAILED, handleTTSFailed)
|
||||
ttsEvents.on(TTS_EVENTS.TTS_CREATED, handleTTSCreated)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('tts-generated', handleTTSGenerated)
|
||||
// Cleanup event listeners
|
||||
ttsEvents.off(TTS_EVENTS.TTS_COMPLETED, handleTTSCompleted)
|
||||
ttsEvents.off(TTS_EVENTS.TTS_FAILED, handleTTSFailed)
|
||||
ttsEvents.off(TTS_EVENTS.TTS_CREATED, handleTTSCreated)
|
||||
}
|
||||
}, [fetchTTSHistory])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user