Add pause feature
This commit is contained in:
@@ -57,3 +57,13 @@ export const showTree = (tree?: q.Tree<TopicViewModel>) => (dispatch: Dispatch<a
|
||||
type: ActionTypes.TREE_SHOW_TREE,
|
||||
})
|
||||
}
|
||||
|
||||
export const togglePause = (tree?: q.Tree<TopicViewModel>) => (dispatch: Dispatch<any>, getState: () => AppState): AnyAction => {
|
||||
const paused = getState().tree.paused
|
||||
const tree = getState().tree.tree
|
||||
// tree && tree.applyUnmergedChanges()
|
||||
|
||||
return dispatch({
|
||||
type: paused ? ActionTypes.TREE_RESUME_UPDATES : ActionTypes.TREE_PAUSE_UPDATES,
|
||||
})
|
||||
}
|
||||
|
||||
108
app/src/components/PauseButton.tsx
Normal file
108
app/src/components/PauseButton.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import * as React from 'react'
|
||||
import * as q from '../../../backend/src/Model'
|
||||
import CustomIconButton from './CustomIconButton'
|
||||
import Pause from '@material-ui/icons/PauseCircleFilled'
|
||||
import Resume from '@material-ui/icons/PlayCircleFilled'
|
||||
import { AppState } from '../reducers'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import { connect } from 'react-redux'
|
||||
import { treeActions } from '../actions'
|
||||
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
|
||||
const styles: StyleRulesCallback = theme => ({ })
|
||||
|
||||
interface Props {
|
||||
classes: any
|
||||
actions: {
|
||||
tree: typeof treeActions,
|
||||
}
|
||||
paused: boolean
|
||||
tree?: q.Tree<any>
|
||||
}
|
||||
|
||||
class PauseButton extends React.Component<Props, {changes: number}> {
|
||||
private timer?: any
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = { changes: 0 }
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.timer = setInterval(() => {
|
||||
if (!this.props.paused || !this.props.tree) {
|
||||
return
|
||||
}
|
||||
|
||||
const changes = this.props.tree.unmergedChanges()
|
||||
if (this.state.changes !== changes.length) {
|
||||
this.setState({ changes: changes.length })
|
||||
}
|
||||
}, 300)
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.timer && clearInterval(this.timer)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { actions, classes } = this.props
|
||||
|
||||
return (
|
||||
<div style={{ display: 'inline-flex' }}>
|
||||
<span>
|
||||
<CustomIconButton onClick={this.props.actions.tree.togglePause} >
|
||||
{this.props.paused ? this.renderResume() : this.renderPause()}
|
||||
</CustomIconButton>
|
||||
</span>
|
||||
{this.props.paused ? this.renderBufferStats() : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private renderResume() {
|
||||
return (
|
||||
<Tooltip title="Resumes updating the tree, after applying all recorded changes">
|
||||
<Resume />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
private renderPause() {
|
||||
return (
|
||||
<Tooltip title="Stops all updates, records changes until the buffer is full.">
|
||||
<Pause />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
private renderBufferStats() {
|
||||
if (!this.props.tree) {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{this.state.changes} changes<br />
|
||||
buffer at {Math.round(this.props.tree.unmergedChanges().fillState() * 10000) / 100}%
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
paused: state.tree.paused,
|
||||
tree: state.tree.tree,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => {
|
||||
return {
|
||||
actions: {
|
||||
tree: bindActionCreators(treeActions, dispatch),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(PauseButton))
|
||||
@@ -7,7 +7,7 @@ import Search from '@material-ui/icons/Search'
|
||||
import { AppState } from '../reducers'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import { connect } from 'react-redux'
|
||||
import { connectionActions, settingsActions } from '../actions'
|
||||
import { connectionActions, settingsActions, treeActions } from '../actions'
|
||||
import { fade } from '@material-ui/core/styles/colorManipulator'
|
||||
import { StyleRulesCallback, withStyles } from '@material-ui/core/styles'
|
||||
import {
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
Toolbar,
|
||||
Typography,
|
||||
} from '@material-ui/core'
|
||||
import PauseButton from './PauseButton';
|
||||
|
||||
const styles: StyleRulesCallback = theme => ({
|
||||
title: {
|
||||
@@ -72,7 +73,7 @@ const styles: StyleRulesCallback = theme => ({
|
||||
disconnect: {
|
||||
margin: 'auto 8px auto auto',
|
||||
color: theme.palette.primary.contrastText,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
interface Props {
|
||||
@@ -101,10 +102,11 @@ class TitleBar extends React.Component<Props, {}> {
|
||||
</IconButton>
|
||||
<Typography className={classes.title} variant="h6" color="inherit">MQTT Explorer</Typography>
|
||||
{this.renderSearch()}
|
||||
<PauseButton />
|
||||
<Button className={classes.disconnect} onClick={actions.connection.disconnect}>
|
||||
Disconnect <CloudOff style={{ marginRight: '8px', paddingLeft: '8px' }}/>
|
||||
</Button>
|
||||
<ConnectionHealthIndicator />
|
||||
<ConnectionHealthIndicator withBackground={true} />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
)
|
||||
|
||||
@@ -25,6 +25,7 @@ interface Props {
|
||||
autoExpandLimit: number
|
||||
highlightTopicUpdates: boolean
|
||||
selectTopicWithMouseOver: boolean
|
||||
paused: boolean
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -78,7 +79,10 @@ class Tree extends React.PureComponent<Props, State> {
|
||||
this.updateTimer && clearTimeout(this.updateTimer)
|
||||
this.updateTimer = undefined
|
||||
this.renderTime = performance.now()
|
||||
this.props.tree && this.props.tree.applyUnmergedChanges()
|
||||
|
||||
if (!this.props.paused) {
|
||||
this.props.tree && this.props.tree.applyUnmergedChanges()
|
||||
}
|
||||
window.requestIdleCallback(() => {
|
||||
this.setState({ lastUpdate: this.renderTime })
|
||||
}, { timeout: 100 })
|
||||
@@ -126,6 +130,7 @@ class Tree extends React.PureComponent<Props, State> {
|
||||
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,
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface TreeState {
|
||||
tree?: q.Tree<TopicViewModel>
|
||||
selectedTopic?: q.TreeNode<TopicViewModel>
|
||||
filter?: string
|
||||
paused: boolean
|
||||
}
|
||||
|
||||
export type Action = ShowTree | SelectTopic
|
||||
@@ -14,6 +15,8 @@ export type Action = ShowTree | SelectTopic
|
||||
export enum ActionTypes {
|
||||
TREE_SHOW_TREE = 'TREE_SHOW_TREE',
|
||||
TREE_SELECT_TOPIC = 'TREE_SELECT_TOPIC',
|
||||
TREE_RESUME_UPDATES = 'TREE_RESUME_UPDATES',
|
||||
TREE_PAUSE_UPDATES = 'TREE_PAUSE_UPDATES',
|
||||
}
|
||||
|
||||
export interface ShowTree {
|
||||
@@ -27,11 +30,26 @@ export interface SelectTopic {
|
||||
selectedTopic?: q.TreeNode<TopicViewModel>
|
||||
}
|
||||
|
||||
const initialState: TreeState = { }
|
||||
export interface SetPause {
|
||||
type: ActionTypes.TREE_PAUSE_UPDATES | ActionTypes.TREE_RESUME_UPDATES
|
||||
}
|
||||
|
||||
const initialState: TreeState = {
|
||||
paused: false,
|
||||
}
|
||||
|
||||
const setPaused = (pause: boolean) => (state: TreeState, action: ShowTree) => {
|
||||
return {
|
||||
...state,
|
||||
paused: pause,
|
||||
}
|
||||
}
|
||||
|
||||
export const treeReducer = createReducer(initialState, {
|
||||
TREE_SHOW_TREE: showTree,
|
||||
TREE_SELECT_TOPIC: selectTopic,
|
||||
TREE_PAUSE_UPDATES: setPaused(true),
|
||||
TREE_RESUME_UPDATES: setPaused(false),
|
||||
})
|
||||
|
||||
function showTree(state: TreeState, action: ShowTree) {
|
||||
|
||||
Reference in New Issue
Block a user