import { TaskComponentState } from "../entities/task" ; import { TaskRepetitionOutcome } from "../event" ; import { Scheduler , SchedulerOutput } from "../scheduler" ; export const defaultSpacedRepetitionSchedulerConfiguration = { intervalGrowthFactor : 2.3 , initialReviewInterval : 1000 * 60 * 60 * 24 * 5 , } ; export interface SpacedRepetitionSchedulerConfiguration { intervalGrowthFactor : number ; initialReviewInterval : number ; } export function createSpacedRepetitionScheduler ( schedulerConfiguration = defaultSpacedRepetitionSchedulerConfiguration ) { return { computeNextDueIntervalMillisForRepetition ( componentState , timestampMillis , outcome ) { const currentReviewIntervalMillis = Math . max ( 0 , timestampMillis - ( componentState . lastRepetitionTimestampMillis ?? componentState . createdAtTimestampMillis ) ) ; let newIntervalMillis ; if ( outcome === TaskRepetitionOutcome . Remembered ) { if ( currentReviewIntervalMillis < componentState . intervalMillis ) { newIntervalMillis = Math . max ( componentState . intervalMillis , schedulerConfiguration . initialReviewInterval , Math . floor ( currentReviewIntervalMillis * schedulerConfiguration . intervalGrowthFactor ) ) ; } else { newIntervalMillis = Math . max ( schedulerConfiguration . initialReviewInterval , Math . floor ( currentReviewIntervalMillis * schedulerConfiguration . intervalGrowthFactor ) ) ; } } else { if ( componentState . intervalMillis < schedulerConfiguration . initialReviewInterval ) { newIntervalMillis = componentState . intervalMillis ; } else { newIntervalMillis = Math . max ( schedulerConfiguration . initialReviewInterval , Math . floor ( componentState . intervalMillis / schedulerConfiguration . intervalGrowthFactor ) ) ; } } const jitter = ( timestampMillis % 1000 ) * ( 60 * 10 ) ; const newDueTimestampMillis = timestampMillis + jitter + ( outcome === TaskRepetitionOutcome . Remembered ? newIntervalMillis : 1000 * 60 * 10 ) ; return { dueTimestampMillis : newDueTimestampMillis , intervalMillis : newIntervalMillis , } ; } , } ; }