github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/app/containers/timewindow/index.tsx (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 import React from "react"; 12 import { connect } from "react-redux"; 13 import moment from "moment"; 14 15 import { AdminUIState } from "src/redux/state"; 16 import * as timewindow from "src/redux/timewindow"; 17 import _ from "lodash"; 18 19 interface TimeWindowManagerProps { 20 // The current timewindow redux state. 21 timeWindow: timewindow.TimeWindowState; 22 // Callback function used to set a new time window. 23 setTimeWindow: typeof timewindow.setTimeWindow; 24 // Optional override method to obtain the current time. Used for tests. 25 now?: () => moment.Moment; 26 } 27 28 interface TimeWindowManagerState { 29 // Identifier from an outstanding call to setTimeout. 30 timeout: number; 31 } 32 33 /** 34 * TimeWindowManager takes responsibility for advancing the current global 35 * time window used by metric graphs. It renders nothing, but will dispatch an 36 * updated time window into the redux store whenever the previous time window is 37 * expired. 38 */ 39 class TimeWindowManager extends React.Component<TimeWindowManagerProps, TimeWindowManagerState> { 40 constructor(props?: TimeWindowManagerProps, context?: any) { 41 super(props, context); 42 this.state = { timeout: null }; 43 } 44 45 /** 46 * checkWindow determines when the current time window will expire. If it is 47 * already expired, a new time window is dispatched immediately. Otherwise, 48 * setTimeout is used to asynchronously set a new time window when the current 49 * one expires. 50 */ 51 checkWindow(props: TimeWindowManagerProps) { 52 // Clear any existing timeout. 53 if (this.state.timeout) { 54 clearTimeout(this.state.timeout); 55 this.setState({ timeout: null }); 56 } 57 58 // If there is no current window, or if scale have changed since this 59 // window was generated, set one immediately. 60 if (!props.timeWindow.currentWindow || props.timeWindow.scaleChanged) { 61 this.setWindow(props); 62 return; 63 } 64 65 // Exact time ranges can't expire. 66 if (props.timeWindow.scale.windowEnd) { 67 // this.setWindow(props); 68 return; 69 } 70 71 const now = props.now ? props.now() : moment(); 72 const currentEnd = props.timeWindow.currentWindow.end; 73 const expires = currentEnd.clone().add(props.timeWindow.scale.windowValid); 74 if (now.isAfter(expires)) { 75 // Current time window is expired, reset it. 76 this.setWindow(props); 77 } else { 78 // Set a timeout to reset the window when the current window expires. 79 const newTimeout = window.setTimeout(() => this.setWindow(props), expires.diff(now).valueOf()); 80 this.setState({ 81 timeout: newTimeout, 82 }); 83 } 84 } 85 86 /** 87 * setWindow dispatches a new time window, extending backwards from the 88 * current time. 89 */ 90 setWindow(props: TimeWindowManagerProps) { 91 if (!props.timeWindow.scale.windowEnd) { 92 if (!props.timeWindow.useTimeRange) { 93 const now = props.now ? props.now() : moment(); 94 props.setTimeWindow({ 95 start: now.clone().subtract(props.timeWindow.scale.windowSize), 96 end: now, 97 }); 98 } 99 } else { 100 const windowEnd = props.timeWindow.scale.windowEnd; 101 props.setTimeWindow({ 102 start: windowEnd.clone().subtract(props.timeWindow.scale.windowSize), 103 end: windowEnd, 104 }); 105 } 106 } 107 108 componentDidMount() { 109 this.checkWindow(this.props); 110 } 111 112 componentDidUpdate(prevProps: TimeWindowManagerProps) { 113 if (!_.isEqual(prevProps.timeWindow, this.props.timeWindow)) { 114 this.checkWindow(this.props); 115 } 116 } 117 118 render(): any { 119 // Render nothing. 120 return null; 121 } 122 } 123 124 const timeWindowManagerConnected = connect( 125 (state: AdminUIState) => { 126 return { 127 timeWindow: state.timewindow, 128 }; 129 }, 130 { 131 setTimeWindow: timewindow.setTimeWindow, 132 }, 133 )(TimeWindowManager); 134 135 export default timeWindowManagerConnected; 136 export { TimeWindowManager as TimeWindowManagerUnconnected };