128 lines
3.5 KiB
TypeScript
128 lines
3.5 KiB
TypeScript
import { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from 'react'
|
|
import { toast } from 'sonner'
|
|
import { jobApi } from '@/lib/api'
|
|
import type { Job, JobStatus } from '@/types/job'
|
|
import { POLL_INTERVAL } from '@/lib/constants'
|
|
import { useHistoryContext } from '@/contexts/HistoryContext'
|
|
|
|
interface JobContextType {
|
|
currentJob: Job | null
|
|
status: JobStatus | null
|
|
error: string | null
|
|
elapsedTime: number
|
|
startJob: (jobId: number) => void
|
|
stopJob: () => void
|
|
resetJob: () => void
|
|
loadCompletedJob: (job: Job) => void
|
|
}
|
|
|
|
const JobContext = createContext<JobContextType | undefined>(undefined)
|
|
|
|
export function JobProvider({ children }: { children: ReactNode }) {
|
|
const [currentJob, setCurrentJob] = useState<Job | null>(null)
|
|
const [status, setStatus] = useState<JobStatus | null>(null)
|
|
const [error, setError] = useState<string | null>(null)
|
|
const [elapsedTime, setElapsedTime] = useState(0)
|
|
|
|
const { refresh: historyRefresh } = useHistoryContext()
|
|
|
|
const stopJob = useCallback(() => {
|
|
setCurrentJob(null)
|
|
setStatus(null)
|
|
setError(null)
|
|
setElapsedTime(0)
|
|
}, [])
|
|
|
|
const resetJob = useCallback(() => {
|
|
setError(null)
|
|
}, [])
|
|
|
|
const loadCompletedJob = useCallback((job: Job) => {
|
|
setCurrentJob(job)
|
|
setStatus(job.status)
|
|
setError(job.error_message || null)
|
|
setElapsedTime(0)
|
|
}, [])
|
|
|
|
const startJob = useCallback((jobId: number) => {
|
|
// Reset state for new job
|
|
setCurrentJob(null)
|
|
setStatus('pending')
|
|
setError(null)
|
|
setElapsedTime(0)
|
|
|
|
let pollInterval: ReturnType<typeof setInterval> | null = null
|
|
let timeInterval: ReturnType<typeof setInterval> | null = null
|
|
|
|
const poll = async () => {
|
|
try {
|
|
const job = await jobApi.getJob(jobId)
|
|
setCurrentJob(job)
|
|
setStatus(job.status)
|
|
|
|
if (job.status === 'completed') {
|
|
if (pollInterval) clearInterval(pollInterval)
|
|
if (timeInterval) clearInterval(timeInterval)
|
|
toast.success('任务完成!')
|
|
try {
|
|
historyRefresh()
|
|
} catch {}
|
|
} else if (job.status === 'failed') {
|
|
if (pollInterval) clearInterval(pollInterval)
|
|
if (timeInterval) clearInterval(timeInterval)
|
|
setError(job.error_message || '任务失败')
|
|
toast.error(job.error_message || '任务失败')
|
|
try {
|
|
historyRefresh()
|
|
} catch {}
|
|
}
|
|
} catch (error: any) {
|
|
if (pollInterval) clearInterval(pollInterval)
|
|
if (timeInterval) clearInterval(timeInterval)
|
|
const message = error.response?.data?.detail || '获取任务状态失败'
|
|
setError(message)
|
|
toast.error(message)
|
|
}
|
|
}
|
|
|
|
poll()
|
|
pollInterval = setInterval(poll, POLL_INTERVAL)
|
|
timeInterval = setInterval(() => {
|
|
setElapsedTime((prev) => prev + 1)
|
|
}, 1000)
|
|
|
|
return () => {
|
|
if (pollInterval) clearInterval(pollInterval)
|
|
if (timeInterval) clearInterval(timeInterval)
|
|
}
|
|
}, [historyRefresh])
|
|
|
|
const value = useMemo(
|
|
() => ({
|
|
currentJob,
|
|
status,
|
|
error,
|
|
elapsedTime,
|
|
startJob,
|
|
stopJob,
|
|
resetJob,
|
|
loadCompletedJob,
|
|
}),
|
|
[currentJob, status, error, elapsedTime, startJob, stopJob, resetJob, loadCompletedJob]
|
|
)
|
|
|
|
return (
|
|
<JobContext.Provider value={value}>
|
|
{children}
|
|
</JobContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useJob() {
|
|
const context = useContext(JobContext)
|
|
if (!context) {
|
|
throw new Error('useJob must be used within JobProvider')
|
|
}
|
|
return context
|
|
}
|