import Stream from "mithril-stream-standalone" ; type TimerCallback = ( ) => unknown ; type TOnFinishFn = ( ) => void ; type TimerState = { timerId ? : number ; startTime ? : number ; remaining : number | undefined ; isPaused : boolean ; callback : TimerCallback ; timeoutFn : ( ) => void ; promise ? : Promise < unknown > ; onDone : TOnFinishFn ; onAbort : TOnFinishFn ; } ; type PatchFn = ( state : TimerState ) => TimerState ; const initialState = { callback : ( ) => { } , isPaused : false , onAbort : ( ) => { } , onDone : ( ) => { } , promise : undefined , remaining : undefined , startTime : undefined , timeoutFn : ( ) => { } , timerId : undefined , } ; const appendStartTimer = ( state , callback , duration , updateState ) => { const timeoutFn = ( ) => { callback ( ) ; state . onDone ( ) ; updateState ( ) ; } ; return { timeoutFn , promise : new Promise < void > ( ( resolve ) => { state . onDone = ( ) => resolve ( ) ; state . onAbort = ( ) => resolve ( ) ; } ) , ... ( state . isPaused ? { } : { startTime : new Date ( ) . getTime ( ) , timerId : window . setTimeout ( timeoutFn , duration ) , remaining : duration , } ) , } ; } ; const appendStopTimeout = ( state ) => { window . clearTimeout ( state . timerId ) ; return { timerId : initialState . timerId , } ; } ; const appendStopTimer = ( state ) => ( { ... appendStopTimeout ( state ) , } ) ; const appendPauseTimer = ( state ) => ( { ... appendStopTimeout ( state ) , isPaused : true , remaining : getRemaining ( state ) , } ) ; const appendResumeTimer = ( state , minimumDuration ? ) => { window . clearTimeout ( state . timerId ) ; const remaining = minimumDuration ? Math . max ( state . remaining || 0 , minimumDuration ) : state . remaining ; return { startTime : new Date ( ) . getTime ( ) , isPaused : false , remaining , timerId : window . setTimeout ( state . timeoutFn , remaining ) , } ; } ; const getRemaining = ( state ) => state . remaining === 0 || state . remaining === undefined ? state . remaining : state . remaining - ( new Date ( ) . getTime ( ) - ( state . startTime || 0 ) ) ; export const TimerStore = ( ) => { const timer = { initialState , actions : ( update ) => ( { param { callback } Function param { duration } Number start : ( callback , duration ) => { update ( ( state ) => ( { ... state , ... appendStopTimeout ( state ) , ... appendStartTimer ( state , callback , duration , ( ) => timer . actions ( update ) . done ( ) ) , ... ( state . isPaused && appendPauseTimer ( state ) ) , } ) ) ; } , /**
             * Stops the timer.
             */ stop : ( ) => { update ( ( state ) => ( { ... state , ... appendStopTimer ( state ) , ... initialState , } ) ) ; } , /**
             * Pauses a running timer.
             */ pause : ( ) => { update ( ( state ) => ( { ... state , ... ( ! state . isPaused && appendPauseTimer ( state ) ) , } ) ) ; } , param { minimumDuration } Number resume : ( minimumDuration ? ) => { update ( ( state ) => ( { ... state , ... ( state . isPaused && appendResumeTimer ( state , minimumDuration ) ) , } ) ) ; } , /**
             * Aborts and clears a timer.
             */ abort : ( ) => { update ( ( state ) => { state . onAbort ( ) ; return { ... state , ... appendStopTimeout ( state ) , } ; } ) ; } , /**
             * Updates the current state. Used to get the state for selectors.getRemaining.
             */ refresh : ( ) => { update ( ( state ) => ( { ... state , } ) ) ; } , /**
             * Brings the timer to its initial state.
             * Used internally.
             */ done : ( ) => { update ( ( ) => initialState ) ; } , } ) , selectors : ( states ) => ( { /**
             * Returns the paused state.
             */ isPaused : ( ) => { const state = states ( ) ; return state . isPaused ; } , /**
             * Returns the remaining duration in milliseconds.
             */ getRemaining : ( ) => { const state = states ( ) ; return state . isPaused ? state . remaining : getRemaining ( state ) ; } , /**
             * The promise that is handled when the timer is done or canceled.
             */ getResultPromise : ( ) => { const state = states ( ) ; return state . promise ; } , } ) , } ; const update = Stream < PatchFn > ( ) ; const states = Stream . scan ( ( state , patch ) => patch ( state ) , { ... timer . initialState , } , update ) ; const actions = { ... timer . actions ( update ) , } ; const selectors = { ... timer . selectors ( states ) , } ; return { states , actions , selectors , } ; } ; export type Timer = ReturnType < typeof TimerStore > ;