150 lines
4.0 KiB
TypeScript
150 lines
4.0 KiB
TypeScript
import * as q from '../../../../backend/src/Model'
|
|
import * as React from 'react'
|
|
import TreeNode from './TreeNode'
|
|
import { AppState } from '../../reducers'
|
|
import { bindActionCreators } from 'redux'
|
|
import { connect } from 'react-redux'
|
|
import { TopicOrder } from '../../reducers/Settings'
|
|
import { TopicViewModel } from '../../model/TopicViewModel'
|
|
import { treeActions } from '../../actions'
|
|
|
|
const MovingAverage = require('moving-average')
|
|
|
|
const averagingTimeInterval = 10 * 1000
|
|
const average = MovingAverage(averagingTimeInterval)
|
|
|
|
declare var window: any
|
|
|
|
interface Props {
|
|
actions: typeof treeActions
|
|
connectionId?: string
|
|
tree?: q.Tree<TopicViewModel>
|
|
filter: string
|
|
host?: string
|
|
topicOrder: TopicOrder
|
|
autoExpandLimit: number
|
|
highlightTopicUpdates: boolean
|
|
selectTopicWithMouseOver: boolean
|
|
paused: boolean
|
|
}
|
|
|
|
interface State {
|
|
lastUpdate: number
|
|
}
|
|
|
|
class Tree extends React.PureComponent<Props, State> {
|
|
private updateTimer?: any
|
|
private perf: number = 0
|
|
private renderTime = 0
|
|
|
|
constructor(props: any) {
|
|
super(props)
|
|
this.state = { lastUpdate: 0 }
|
|
}
|
|
|
|
private performanceCallback = (ms: number) => {
|
|
average.push(Date.now(), ms)
|
|
}
|
|
|
|
public time(): number {
|
|
const time = performance.now() - this.perf
|
|
this.perf = performance.now()
|
|
|
|
return time
|
|
}
|
|
|
|
public componentWillReceiveProps(nextProps: Props) {
|
|
if (this.props.tree !== nextProps.tree) {
|
|
if (this.props.tree) {
|
|
this.props.tree.didReceive.unsubscribe(this.throttledTreeUpdate)
|
|
}
|
|
if (nextProps.tree) {
|
|
nextProps.tree.didReceive.subscribe(this.throttledTreeUpdate)
|
|
}
|
|
this.setState(this.state)
|
|
}
|
|
}
|
|
|
|
public componentWillUnmount() {
|
|
this.props.tree && this.props.tree.didReceive.unsubscribe(this.throttledTreeUpdate)
|
|
}
|
|
|
|
public throttledTreeUpdate = () => {
|
|
if (this.updateTimer) {
|
|
return
|
|
}
|
|
|
|
const expectedRenderTime = average.forecast()
|
|
const updateInterval = Math.max(expectedRenderTime * 7, 300)
|
|
const timeUntilNextUpdate = updateInterval - (performance.now() - this.renderTime)
|
|
|
|
this.updateTimer = setTimeout(() => {
|
|
window.requestIdleCallback(() => {
|
|
this.updateTimer && clearTimeout(this.updateTimer)
|
|
this.updateTimer = undefined
|
|
this.renderTime = performance.now()
|
|
|
|
if (!this.props.paused) {
|
|
this.props.tree && this.props.tree.applyUnmergedChanges()
|
|
}
|
|
window.requestIdleCallback(() => {
|
|
this.setState({ lastUpdate: this.renderTime })
|
|
}, { timeout: 100 })
|
|
}, { timeout: 500 })
|
|
}, Math.max(0, timeUntilNextUpdate))
|
|
}
|
|
|
|
public render() {
|
|
const { tree, filter } = this.props
|
|
if (!tree) {
|
|
return null
|
|
}
|
|
|
|
const style: React.CSSProperties = {
|
|
lineHeight: '1.1',
|
|
cursor: 'default',
|
|
}
|
|
|
|
return (
|
|
<div style={style}>
|
|
<TreeNode
|
|
key={tree.hash()}
|
|
animateChages={true}
|
|
isRoot={true}
|
|
treeNode={tree}
|
|
name={this.props.host}
|
|
collapsed={false}
|
|
performanceCallback={this.performanceCallback}
|
|
autoExpandLimit={this.props.autoExpandLimit}
|
|
topicOrder={this.props.topicOrder}
|
|
lastUpdate={tree.lastUpdate}
|
|
didSelectTopic={this.props.actions.selectTopic}
|
|
highlightTopicUpdates={this.props.highlightTopicUpdates}
|
|
selectTopicWithMouseOver={this.props.selectTopicWithMouseOver}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = (state: AppState) => {
|
|
return {
|
|
tree: state.tree.tree,
|
|
paused: state.tree.paused,
|
|
filter: state.tree.filter,
|
|
host: state.connection.host,
|
|
autoExpandLimit: state.settings.autoExpandLimit,
|
|
topicOrder: state.settings.topicOrder,
|
|
highlightTopicUpdates: state.settings.highlightTopicUpdates,
|
|
selectTopicWithMouseOver: state.settings.selectTopicWithMouseOver,
|
|
}
|
|
}
|
|
|
|
const mapDispatchToProps = (dispatch: any) => {
|
|
return {
|
|
actions: bindActionCreators(treeActions, dispatch),
|
|
}
|
|
}
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(Tree)
|