diff --git a/app/package-lock.json b/app/package-lock.json index 63494af..862331b 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -4172,14 +4172,15 @@ } }, "react": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/react/-/react-16.3.2.tgz", - "integrity": "sha512-o5GPdkhciQ3cEph6qgvYB7LTOHw/GB0qRI6ZFNugj49qJCFfgHwVNjZ5u+b7nif4vOeMIOuYj3CeYe2IBD74lg==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.7.0.tgz", + "integrity": "sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==", + "dev": true, "requires": { - "fbjs": "^0.8.16", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.2", + "scheduler": "^0.12.0" } }, "react-base16-styling": { @@ -4194,14 +4195,15 @@ } }, "react-dom": { - "version": "16.3.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.3.3.tgz", - "integrity": "sha512-ALCp7ZbSGkqRDtQoZozKVNgwXMxbxf/IGOUMC2A0yF6JHeZrS8e2cOotPT87Vf4b7PKCuUVKU4/RDEXxToA/yA==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.7.0.tgz", + "integrity": "sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==", + "dev": true, "requires": { - "fbjs": "^0.8.16", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.2", + "scheduler": "^0.12.0" } }, "react-event-listener": { @@ -4438,6 +4440,16 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "scheduler": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.12.0.tgz", + "integrity": "sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "schema-utils": { "version": "0.4.7", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", diff --git a/app/package.json b/app/package.json index f32bdc9..1022bdd 100644 --- a/app/package.json +++ b/app/package.json @@ -15,8 +15,9 @@ "@types/react": "^16.7.18", "@types/react-dom": "^16.0.11", "@types/react-redux": "^6.0.12", + "react": "^16.7.0", + "react-dom": "^16.7.0", "react-redux": "^6.0.0", - "react-transition-group": "^2.5.2", "redux": "^4.0.1", "webpack-livereload-plugin": "^2.2.0" }, @@ -31,8 +32,6 @@ "jquery": "^3.3.1", "lodash.throttle": "^4.1.1", "moving-average": "^1.0.0", - "react": "^16.3.2", - "react-dom": "^16.3.3", "react-json-view": "^1.19.1", "sha1": "^1.1.1", "socket.io-client": "^2.2.0", diff --git a/app/src/actions/Settings.ts b/app/src/actions/Settings.ts index 1eff351..3fdfc4a 100644 --- a/app/src/actions/Settings.ts +++ b/app/src/actions/Settings.ts @@ -1,4 +1,4 @@ -import { ActionTypes } from '../reducers' +import { ActionTypes, NodeOrder } from '../reducers' export const setAutoExpandLimit = (autoExpandLimit: number = 0) => { return { @@ -12,3 +12,10 @@ export const toggleSettingsVisibility = () => { type: ActionTypes.toggleSettingsVisibility, } } + +export const setNodeOrder = (nodeOrder: NodeOrder = NodeOrder.none) => { + return { + nodeOrder, + type: ActionTypes.setNodeOrder, + } +} diff --git a/app/src/components/ConnectionSetup/Notification.tsx b/app/src/components/ConnectionSetup/Notification.tsx index 44b9a55..d3aed67 100644 --- a/app/src/components/ConnectionSetup/Notification.tsx +++ b/app/src/components/ConnectionSetup/Notification.tsx @@ -42,13 +42,13 @@ class Notification extends React.Component { vertical: 'bottom', horizontal: 'left', }} - open = {this.state.snackBarOpen} - autoHideDuration = {6000} - onClose = {() => { this.setState({ snackBarOpen: false }) }} + open={this.state.snackBarOpen} + autoHideDuration={6000} + onClose={() => { this.setState({ snackBarOpen: false }) }} > } diff --git a/app/src/components/Copy.tsx b/app/src/components/Copy.tsx index ce062fe..e92525c 100644 --- a/app/src/components/Copy.tsx +++ b/app/src/components/Copy.tsx @@ -32,8 +32,8 @@ class Copy extends React.Component { public render() { const icon = !this.state.didCopy - ? - : + ? + : return {icon} diff --git a/app/src/components/Settings.tsx b/app/src/components/Settings.tsx index 140cda2..f5847d9 100644 --- a/app/src/components/Settings.tsx +++ b/app/src/components/Settings.tsx @@ -14,7 +14,7 @@ import { Typography, InputBase, Input, InputLabel } from '@material-ui/core' import { withStyles, StyleRulesCallback } from '@material-ui/core/styles' import { settingsActions } from '../actions' -import { AppState } from '../reducers' +import { AppState, NodeOrder } from '../reducers' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' @@ -40,16 +40,14 @@ const styles: StyleRulesCallback = theme => ({ interface Props { actions?: any - autoExpandLimit: number, - visible: boolean, - store?: any, + autoExpandLimit: number + visible: boolean + store?: any classes: any + nodeOrder: NodeOrder } -interface State { -} - -class Settings extends React.Component { +class Settings extends React.Component { constructor(props: any) { super(props) this.state = {} @@ -57,59 +55,91 @@ class Settings extends React.Component { public render() { const { classes, actions, visible } = this.props - return -
e.stopPropagation()} - onKeyDown = {(e: React.KeyboardEvent) => e.stopPropagation()} + return ( + +
e.stopPropagation()} + onKeyDown={(e: React.KeyboardEvent) => e.stopPropagation()} + > - - - - - Settings - - + + + + + Settings + + - {this.renderAutoExpand()} -
-
+ {this.renderAutoExpand()} + {this.renderNodeOrder()} +
+
+ ) } private renderAutoExpand() { const { classes, actions, autoExpandLimit } = this.props - return
- Auto Expand + return ( +
+ Auto Expand + } + displayEmpty={true} + name="auto-expand" + className={classes.input} + > + Disabled + Few + Some + Most + All + +
+ ) + } + + private renderNodeOrder() { + const { classes, actions, nodeOrder } = this.props + + return ( +
+ Topic order } - displayEmpty - name = "auto-expand" - className = {classes.input} + value={nodeOrder} + onChange={ (e: React.ChangeEvent) => { + window.requestAnimationFrame(() => { + actions.setNodeOrder(e.target.value) + }) + }} + input={} + displayEmpty={true} + name="node-order" + className={classes.input} > - Disabled - Few - Some - Most - All + default + a-z + {NodeOrder.messages} + {NodeOrder.topics} -
+
) } } const mapStateToProps = (state: AppState) => { return { autoExpandLimit: state.settings.autoExpandLimit, + nodeOrder: state.settings.nodeOrder, visible: state.settings.visible, } } diff --git a/app/src/components/Tree/Tree.tsx b/app/src/components/Tree/Tree.tsx index 3bdba4a..4cb4c58 100644 --- a/app/src/components/Tree/Tree.tsx +++ b/app/src/components/Tree/Tree.tsx @@ -9,6 +9,7 @@ import { AppState } from '../../reducers' const MovingAverage = require('moving-average') declare const performance: any +declare const window: any const timeInterval = 10 * 1000 const average = MovingAverage(timeInterval) @@ -48,14 +49,17 @@ class Tree extends React.Component { } const expectedRenderTime = average.forecast() - const updateInterval = Math.max(expectedRenderTime * 5, 300) + const updateInterval = Math.max(expectedRenderTime * 20, 300) const timeUntilNextUpdate = updateInterval - (performance.now() - this.lastUpdate) this.updateTimer = setTimeout(() => { - this.lastUpdate = performance.now() - this.updateTimer && clearTimeout(this.updateTimer) - this.updateTimer = undefined - this.setState(state) + window.requestAnimationFrame(() => { + console.log('doRender') + this.lastUpdate = performance.now() + this.updateTimer && clearTimeout(this.updateTimer) + this.updateTimer = undefined + this.setState(state) + }) }, Math.max(0, timeUntilNextUpdate)) } @@ -98,15 +102,15 @@ class Tree extends React.Component { cursor: 'default', } - return + return { average.push(Date.now(), ms) diff --git a/app/src/components/Tree/TreeNode.tsx b/app/src/components/Tree/TreeNode.tsx index 64ce467..b061052 100644 --- a/app/src/components/Tree/TreeNode.tsx +++ b/app/src/components/Tree/TreeNode.tsx @@ -136,38 +136,41 @@ class TreeNode extends React.Component { const { classes } = this.props this.dirtyEdges = this.dirtyMessage = this.dirtySubnodes = false - return
{ - event.stopPropagation() - this.toggle() - this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode) - }} - > - - this.toggle()} - collapsed={this.collapsed()} - treeNode={this.props.treeNode} - name={this.props.name} - didSelectNode={this.props.didSelectNode} - toggleCollapsed={() => this.toggle()} - /> - - { this.renderNodes() } -
+ return ( +
+ + + + {this.renderNodes()} +
+ ) + } + + private didClickNode = (event: React.MouseEvent) => { + event.stopPropagation() + this.toggle() + this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode) } private renderNodes() { - return this.toggle()} - treeNode={this.props.treeNode} - /> + return ( + + ) } private indicatingChangeAnimationStyle(): React.CSSProperties { diff --git a/app/src/components/Tree/TreeNodeSubnodes.tsx b/app/src/components/Tree/TreeNodeSubnodes.tsx index 964e42f..3f3cad5 100644 --- a/app/src/components/Tree/TreeNodeSubnodes.tsx +++ b/app/src/components/Tree/TreeNodeSubnodes.tsx @@ -1,52 +1,75 @@ import * as React from 'react' +import { connect } from 'react-redux' import * as q from '../../../../backend/src/Model' import { withTheme, Theme } from '@material-ui/core/styles' +import { AppState, NodeOrder } from '../../reducers' import TreeNode from './TreeNode' export interface Props { + nodeOrder?: NodeOrder animateChanges: boolean treeNode: q.TreeNode autoExpandLimit: number collapsed?: boolean | undefined didSelectNode?: (node: q.TreeNode) => void - toggleCollapsed: () => void theme: Theme } class TreeNodeSubnodes extends React.Component { + private sortedNodes(): q.TreeNode[] { + const { nodeOrder, treeNode } = this.props + + let edges = Object.values(treeNode.edges) + if (nodeOrder === NodeOrder.abc) { + edges = edges.sort((a, b) => a.name.localeCompare(b.name)) + } + + let nodes = edges.map(edge => edge.target) + if (nodeOrder === NodeOrder.messages) { + nodes = nodes.sort((a, b) => b.leafMessageCount() - a.leafMessageCount()) + } + if (nodeOrder === NodeOrder.topics) { + nodes = nodes.sort((a, b) => b.leafCount() - a.leafCount()) + } + + return nodes + } + public render() { const edges = Object.values(this.props.treeNode.edges) + if (edges.length === 0 || this.props.collapsed) { + return null + } + const listItemStyle = { padding: '3px 8px 0px 8px', } - if (edges.length > 0 && !this.props.collapsed) { - const listItems = edges - .map(edge => edge.target) - .map(node => ( -
- -
- )) + const nodes = this.sortedNodes() + const listItems = nodes.map(node => ( +
+ +
+ )) - return + return ( + {this.props.collapsed ? null : listItems} - } - - return null + ) } } -export default withTheme()(TreeNodeSubnodes) +const mapStateToProps = (state: AppState) => { + return { + nodeOrder: state.settings.nodeOrder, + } +} + +export default withTheme()(connect(mapStateToProps)(TreeNodeSubnodes)) diff --git a/app/src/components/Tree/TreeNodeTitle.tsx b/app/src/components/Tree/TreeNodeTitle.tsx index d16baf9..bf00439 100644 --- a/app/src/components/Tree/TreeNodeTitle.tsx +++ b/app/src/components/Tree/TreeNodeTitle.tsx @@ -6,7 +6,6 @@ export interface TreeNodeProps extends React.HTMLAttributes { treeNode: q.TreeNode name?: string | undefined collapsed?: boolean | undefined - toggleCollapsed: () => void didSelectNode?: (node: q.TreeNode) => void theme: Theme } @@ -24,25 +23,22 @@ class TreeNodeTitle extends React.Component { } } + private didSelectNode = () => { + if (this.props.treeNode.message) { + this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode) + } + } + public render() { const style: React.CSSProperties = { lineHeight: '1em', whiteSpace: 'nowrap', } - return { - this.toggle() - this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode) - }} - onMouseOver={() => { - if (this.props.treeNode.message) { - this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode) - } - }} - > - {this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()} - + return ( + + {this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()} + + ) } private renderSourceEdge() { @@ -67,16 +63,10 @@ class TreeNodeTitle extends React.Component { display: 'inline-block', } return this.props.treeNode.message - ? = {this.props.treeNode.message.value.toString()} + ? = {this.props.treeNode.message.value.toString()} : null } - private toggle() { - this.props.toggleCollapsed() - } - private renderExpander() { if (this.props.treeNode.edgeCount() === 0) { return null diff --git a/app/src/index.tsx b/app/src/index.tsx index 7a0d5d4..c573ae5 100644 --- a/app/src/index.tsx +++ b/app/src/index.tsx @@ -4,7 +4,7 @@ import { Provider } from 'react-redux' import { createStore } from 'redux' import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles' -import reducers from './reducers' +import reducers, { NodeOrder } from './reducers' import App from './App' declare var document: any @@ -12,6 +12,7 @@ declare var document: any const initialAppState = { settings: { autoExpandLimit: 0, + nodeOrder: NodeOrder.none, visible: false, }, } diff --git a/app/src/reducers/index.ts b/app/src/reducers/index.ts index 1563dee..89a2a41 100644 --- a/app/src/reducers/index.ts +++ b/app/src/reducers/index.ts @@ -3,20 +3,30 @@ import { Reducer, Action } from 'redux' export enum ActionTypes { setAutoExpandLimit = 'SET_AUTO_EXPAND_LIMIT', toggleSettingsVisibility = 'TOGGLE_SETTINGS_VISIBILITY', + setNodeOrder = 'SET_NODE_ORDER', } interface SettingsAction extends Action { type: ActionTypes, - autoExpandLimit: number + autoExpandLimit?: number + nodeOrder?: NodeOrder } export interface AppState { settings: SettingsModel } +export enum NodeOrder { + none = 'none', + messages = '#messages', + abc = 'abc', + topics = '#topics', +} + export interface SettingsModel { autoExpandLimit: number visible: boolean + nodeOrder: NodeOrder } const reducer: Reducer = (state, action) => { @@ -27,11 +37,15 @@ const reducer: Reducer = (state, action) = switch (action.type) { case ActionTypes.setAutoExpandLimit: + if (!action.autoExpandLimit) { + return state + } return { ...state, settings: { visible: state.settings.visible, autoExpandLimit: action.autoExpandLimit, + nodeOrder: state.settings.nodeOrder, }, } case ActionTypes.toggleSettingsVisibility: @@ -40,6 +54,19 @@ const reducer: Reducer = (state, action) = settings: { visible: !state.settings.visible, autoExpandLimit: state.settings.autoExpandLimit, + nodeOrder: state.settings.nodeOrder, + }, + } + case ActionTypes.setNodeOrder: + if (!action.nodeOrder) { + return state + } + return { + ...state, + settings: { + visible: state.settings.visible, + autoExpandLimit: state.settings.autoExpandLimit, + nodeOrder: action.nodeOrder, }, } default: diff --git a/build.ts b/build.ts index 2578700..5ac8c8a 100644 --- a/build.ts +++ b/build.ts @@ -27,13 +27,13 @@ const mac: builder.CliOptions = { arm64: false, mac: ['dmg'], projectDir: './build/clean', - publish: 'onTag', + publish: 'always', } async function buildAll() { - await builder.build(linux) +// await builder.build(linux) +// await builder.build(win) await builder.build(mac) - await builder.build(win) } buildAll() diff --git a/package.json b/package.json index ce98184..9b36716 100644 --- a/package.json +++ b/package.json @@ -18,11 +18,7 @@ "appId": "mqtt-explorer", "mac": { "category": "de.t7n.apps.mq-explorer", - "files": [ - "**/*", - "!**/*.ts", - "!app/node_modules" - ] + "publish": ["github"] }, "linux": { "category": "Development", diff --git a/release.sh b/release.sh index eac674a..a78fb4b 100755 --- a/release.sh +++ b/release.sh @@ -19,6 +19,7 @@ exit 0 docker run --rm -ti \ --env ELECTRON_CACHE="/root/.cache/electron" \ --env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \ + --env GH_TOKEN="$GH_TOKEN" \ -v ${PWD}:/project \ -v ~/.cache/electron:/root/.cache/electron \ -v ~/.cache/electron-builder:/root/.cache/electron-builder \ diff --git a/tslint.json b/tslint.json index 283120c..bf60367 100644 --- a/tslint.json +++ b/tslint.json @@ -1,5 +1,5 @@ { - "extends": "tslint-config-airbnb", + "extends": ["tslint-config-airbnb", "tslint-react"], "rules": { "semicolon": [true, "never"], "max-line-length": [true, 200],