Spaces:
Sleeping
Sleeping
File size: 2,885 Bytes
af37c1f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import { useState, useRef, useCallback } from 'react'
import type { SimResult, SimStep } from '../types'
interface SimState {
result: SimResult | null
currentStepIndex: number
isPlaying: boolean
isLoading: boolean
error: string | null
}
const SPEED_MS: Record<string, number> = { slow: 2000, normal: 1000, fast: 400 }
export function useSimulation() {
const [state, setState] = useState<SimState>({
result: null,
currentStepIndex: -1,
isPlaying: false,
isLoading: false,
error: null,
})
const [speed, setSpeed] = useState<'slow' | 'normal' | 'fast'>('normal')
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
const stopTimer = () => {
if (timerRef.current) {
clearInterval(timerRef.current)
timerRef.current = null
}
}
const startPlayback = useCallback((_result: SimResult, fromIndex = 0) => {
stopTimer()
setState(s => ({ ...s, currentStepIndex: fromIndex, isPlaying: true }))
timerRef.current = setInterval(() => {
setState(s => {
if (!s.result) return s
const next = s.currentStepIndex + 1
if (next >= s.result.steps.length) {
stopTimer()
return { ...s, isPlaying: false }
}
return { ...s, currentStepIndex: next }
})
}, SPEED_MS[speed])
}, [speed])
const load = useCallback(async (
fetcher: () => Promise<SimResult>,
) => {
stopTimer()
setState({ result: null, currentStepIndex: -1, isPlaying: false, isLoading: true, error: null })
try {
const result = await fetcher()
setState({ result, currentStepIndex: 0, isPlaying: false, isLoading: false, error: null })
startPlayback(result, 0)
} catch (e) {
const message = e instanceof Error ? e.message : String(e)
setState(s => ({ ...s, isLoading: false, error: message }))
}
}, [startPlayback])
const pause = useCallback(() => {
stopTimer()
setState(s => ({ ...s, isPlaying: false }))
}, [])
const play = useCallback(() => {
setState(s => {
if (!s.result) return s
startPlayback(s.result, s.currentStepIndex)
return s
})
}, [startPlayback])
const reset = useCallback(() => {
stopTimer()
setState(s => ({ ...s, currentStepIndex: 0, isPlaying: false }))
}, [])
const seekTo = useCallback((idx: number) => {
stopTimer()
setState(s => ({ ...s, currentStepIndex: idx, isPlaying: false }))
}, [])
const currentStep: SimStep | null =
state.result && state.currentStepIndex >= 0
? state.result.steps[state.currentStepIndex] ?? null
: null
return {
result: state.result,
currentStep,
currentStepIndex: state.currentStepIndex,
isPlaying: state.isPlaying,
isLoading: state.isLoading,
error: state.error,
speed,
setSpeed,
load,
play,
pause,
reset,
seekTo,
}
}
|