import * as React from 'react' ; import moment from 'moment-timezone' ; import type { Moment } from 'moment-timezone' ; import delay from 'delay' ; export function msUntilIntervalRepeat ( now , interval ) { return interval - ( now % interval ) ; } type Props = { interval : number ; timezone ? : string ; invoke ? : ( ) => mixed ; } & ( { moment : true ; render : ( state : { now : Moment ; loading : boolean ; refresh : ( ) => void ; } ) => React . Node ; } | { moment : false ; render : ( state : { now : Date ; loading : boolean ; refresh : ( ) => void ; } ) => React . Node ; } ) ; type State = { now : Date ; loading : boolean ; } ; export class Timer extends React . Component < Props , State > { _timeoutId ? : TimeoutID ; _intervalId ? : IntervalID ; state = { now : new Date ( ) , loading : false , } ; componentDidMount ( ) { let { interval } = this . props ; let nowMs = this . state . now . getTime ( ) ; let untilNextInterval = msUntilIntervalRepeat ( nowMs , interval ) ; this . _timeoutId = setTimeout ( ( ) => { this . updateTime ( ) ; this . _intervalId = setInterval ( this . updateTime , interval ) ; } , untilNextInterval ) ; } componentWillUnmount ( ) { this . _timeoutId != null && clearTimeout ( this . _timeoutId ) ; this . _intervalId != null && clearInterval ( this . _intervalId ) ; } updateTime = ( ) => { this . setState ( ( ) => ( { now : new Date ( ) } ) ) ; } ; _refresh = async ( ) => { let start = Date . now ( ) ; this . setState ( ( ) => ( { loading : true } ) ) ; this . updateTime ( ) ; let elapsed = Date . now ( ) - start ; if ( elapsed < 500 ) { await delay ( 500 - elapsed ) ; } this . setState ( ( ) => ( { loading : false } ) ) ; } ; refresh = ( ) => { this . _refresh ( ) ; } ; render ( ) { let { now , loading } = this . state ; if ( this . props . moment ) { now = moment ( now ) ; if ( this . props . timezone ) { now = now . tz ( this . props . timezone ) ; } } if ( this . props . invoke ) { this . props . invoke ( ) ; } return this . props . render ( { now , loading , refresh : this . refresh } ) ; } }