VetNet-LCPS / vetcopilot-app /src /hooks /useIdleTimer.ts
thomasm291678
VetCopilot-LCPS: LCPS 2.0 本地推理引擎替换 DeepSeek API
793e953
Raw History Blame Contribute Delete
2.59 kB
/**
* useIdleTimer Hook
*
* 检测用户空闲状态(默认 5 分钟无操作)。
* 监听鼠标移动、键盘按下、触摸等事件,空闲时触发 onIdle 回调。
*
* 用法:
* const { isIdle, resetTimer } = useIdleTimer({
* timeout: 5 * 60 * 1000, // 5 分钟
* onIdle: () => console.log('用户空闲'),
* onActive: () => console.log('用户恢复活动'),
* })
*/
import { useState, useEffect, useCallback, useRef } from 'react'
interface UseIdleTimerOptions {
/** 空闲超时时间(ms),默认 5 分钟 */
timeout?: number
/** 空闲时触发 */
onIdle?: () => void
/** 从空闲恢复活动时触发 */
onActive?: () => void
/** 是否启用,默认 true */
enabled?: boolean
}
interface UseIdleTimerReturn {
/** 当前是否空闲 */
isIdle: boolean
/** 手动重置空闲计时器 */
resetTimer: () => void
}
const EVENTS = ['mousemove', 'mousedown', 'keydown', 'touchstart', 'scroll', 'wheel'] as const
export function useIdleTimer(options: UseIdleTimerOptions = {}): UseIdleTimerReturn {
const { timeout = 5 * 60 * 1000, onIdle, onActive, enabled = true } = options
const [isIdle, setIsIdle] = useState(false)
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const isIdleRef = useRef(false)
const onIdleRef = useRef(onIdle)
const onActiveRef = useRef(onActive)
onIdleRef.current = onIdle
onActiveRef.current = onActive
const clearTimer = useCallback(() => {
if (timerRef.current) {
clearTimeout(timerRef.current)
timerRef.current = null
}
}, [])
const startTimer = useCallback(() => {
clearTimer()
if (!enabled) return
timerRef.current = setTimeout(() => {
isIdleRef.current = true
setIsIdle(true)
onIdleRef.current?.()
}, timeout)
}, [timeout, enabled, clearTimer])
const resetTimer = useCallback(() => {
if (!enabled) return
if (isIdleRef.current) {
isIdleRef.current = false
setIsIdle(false)
onActiveRef.current?.()
}
startTimer()
}, [enabled, startTimer])
useEffect(() => {
if (!enabled) {
clearTimer()
return
}
startTimer()
const handleActivity = () => {
resetTimer()
}
EVENTS.forEach((event) => {
window.addEventListener(event, handleActivity, { passive: true })
})
return () => {
clearTimer()
EVENTS.forEach((event) => {
window.removeEventListener(event, handleActivity)
})
}
}, [enabled, startTimer, resetTimer, clearTimer])
return { isIdle, resetTimer }
}