Compare commits

...

2 Commits

Author SHA1 Message Date
JSC
8f233aaef7 feat: implement extraction event handling and update extraction list on status changes
Some checks failed
Frontend CI / lint (push) Failing after 17s
Frontend CI / build (push) Has been skipped
2025-08-25 10:58:10 +02:00
JSC
e029a692a6 feat: add functionality to fetch and display ongoing extractions with toast notifications 2025-08-24 13:43:52 +02:00
4 changed files with 83 additions and 5 deletions

View File

@@ -9,14 +9,17 @@ import { Socket, io } from 'socket.io-client'
import { toast } from 'sonner'
import {
AUTH_EVENTS,
EXTRACTION_EVENTS,
PLAYER_EVENTS,
SOUND_EVENTS,
USER_EVENTS,
authEvents,
extractionEvents,
playerEvents,
soundEvents,
userEvents,
} from '../lib/events'
import { extractionsService } from '../lib/api/services/extractions'
import { useAuth } from './AuthContext'
interface SocketContextType {
@@ -39,6 +42,22 @@ export function SocketProvider({ children }: SocketProviderProps) {
const [connectionError, setConnectionError] = useState<string | null>(null)
const [isReconnecting, setIsReconnecting] = useState(false)
const fetchAndShowOngoingExtractions = useCallback(async () => {
try {
const processingExtractions = await extractionsService.getProcessingExtractions()
processingExtractions.forEach(extraction => {
const title = extraction.title || 'Processing extraction...'
toast.loading(`Extracting: ${title}`, {
id: `extraction-${extraction.id}`,
duration: Infinity, // Keep it open until status changes
})
})
} catch (error) {
console.error('Failed to fetch ongoing extractions:', error)
}
}, [])
const createSocket = useCallback(() => {
if (!user) return null
@@ -59,6 +78,9 @@ export function SocketProvider({ children }: SocketProviderProps) {
setIsConnected(true)
setConnectionError(null)
setIsReconnecting(false)
// Fetch and show any ongoing extractions
fetchAndShowOngoingExtractions()
})
newSocket.on('disconnect', () => {
@@ -107,6 +129,10 @@ export function SocketProvider({ children }: SocketProviderProps) {
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':
toast.loading(`Extracting: ${title}`, {
@@ -119,6 +145,7 @@ export function SocketProvider({ children }: SocketProviderProps) {
toast.success(`Extraction complete: ${title}`, {
duration: 4000,
})
extractionEvents.emit(EXTRACTION_EVENTS.EXTRACTION_COMPLETED, data)
break
case 'failed':
toast.dismiss(`extraction-${extraction_id}`)
@@ -126,12 +153,13 @@ export function SocketProvider({ children }: SocketProviderProps) {
description: error,
duration: 6000,
})
extractionEvents.emit(EXTRACTION_EVENTS.EXTRACTION_FAILED, data)
break
}
})
return newSocket
}, [user])
}, [user, fetchAndShowOngoingExtractions])
// Handle token refresh - reconnect socket with new token
const handleTokenRefresh = useCallback(() => {

View File

@@ -125,6 +125,16 @@ export class ExtractionsService {
const response = await apiClient.get<GetExtractionsResponse>(url)
return response
}
/**
* Get currently processing extractions
*/
async getProcessingExtractions(): Promise<ExtractionInfo[]> {
const response = await apiClient.get<ExtractionInfo[]>(
'/api/v1/extractions/processing/current'
)
return response
}
}
export const extractionsService = new ExtractionsService()

View File

@@ -36,6 +36,7 @@ export const authEvents = new EventEmitter()
export const playerEvents = new EventEmitter()
export const soundEvents = new EventEmitter()
export const userEvents = new EventEmitter()
export const extractionEvents = new EventEmitter()
// Auth event types
export const AUTH_EVENTS = {
@@ -60,3 +61,11 @@ export const SOUND_EVENTS = {
export const USER_EVENTS = {
USER_CREDITS_CHANGED: 'user_credits_changed',
} as const
// Extraction event types
export const EXTRACTION_EVENTS = {
EXTRACTION_STATUS_UPDATED: 'extraction_status_updated',
EXTRACTION_CREATED: 'extraction_created',
EXTRACTION_COMPLETED: 'extraction_completed',
EXTRACTION_FAILED: 'extraction_failed',
} as const

View File

@@ -15,7 +15,8 @@ import {
type ExtractionStatus,
extractionsService,
} from '@/lib/api/services/extractions'
import { useEffect, useState } from 'react'
import { EXTRACTION_EVENTS, extractionEvents } from '@/lib/events'
import { useCallback, useEffect, useState } from 'react'
import { toast } from 'sonner'
export function ExtractionsPage() {
@@ -51,7 +52,7 @@ export function ExtractionsPage() {
return () => clearTimeout(handler)
}, [searchQuery])
const fetchExtractions = async () => {
const fetchExtractions = useCallback(async () => {
try {
setLoading(true)
setError(null)
@@ -74,11 +75,11 @@ export function ExtractionsPage() {
} finally {
setLoading(false)
}
}
}, [debouncedSearchQuery, sortBy, sortOrder, statusFilter, currentPage, pageSize])
useEffect(() => {
fetchExtractions()
}, [debouncedSearchQuery, sortBy, sortOrder, statusFilter, currentPage, pageSize])
}, [fetchExtractions])
// Reset to page 1 when filters change
useEffect(() => {
@@ -87,6 +88,33 @@ export function ExtractionsPage() {
}
}, [debouncedSearchQuery, sortBy, sortOrder, statusFilter, pageSize])
// Listen for extraction events to refresh the list
useEffect(() => {
const handleExtractionStatusUpdate = () => {
fetchExtractions()
}
const handleExtractionCompleted = () => {
fetchExtractions()
}
const handleExtractionFailed = () => {
fetchExtractions()
}
// Subscribe to extraction events
extractionEvents.on(EXTRACTION_EVENTS.EXTRACTION_STATUS_UPDATED, handleExtractionStatusUpdate)
extractionEvents.on(EXTRACTION_EVENTS.EXTRACTION_COMPLETED, handleExtractionCompleted)
extractionEvents.on(EXTRACTION_EVENTS.EXTRACTION_FAILED, handleExtractionFailed)
return () => {
// Cleanup event listeners
extractionEvents.off(EXTRACTION_EVENTS.EXTRACTION_STATUS_UPDATED, handleExtractionStatusUpdate)
extractionEvents.off(EXTRACTION_EVENTS.EXTRACTION_COMPLETED, handleExtractionCompleted)
extractionEvents.off(EXTRACTION_EVENTS.EXTRACTION_FAILED, handleExtractionFailed)
}
}, [fetchExtractions])
const handlePageChange = (page: number) => {
setCurrentPage(page)
}
@@ -112,6 +140,9 @@ export function ExtractionsPage() {
setUrl('')
setShowCreateDialog(false)
// Emit event for new extraction created
extractionEvents.emit(EXTRACTION_EVENTS.EXTRACTION_CREATED, response.extraction)
// Refresh the extractions list
fetchExtractions()
} catch (err) {