Screw up looks, greatly improve performance
This commit is contained in:
10
app/package-lock.json
generated
10
app/package-lock.json
generated
@@ -1167,6 +1167,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
|
||||||
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
|
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
|
||||||
},
|
},
|
||||||
|
"copy-text-to-clipboard": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-4hDE+0bgqm4G/nXnt91CP3rc0vOptaePPU5WfVZuhv2AYNJogdLHR4pF1XPgXDAGY4QCzj9pD7zKATa+50sQPg=="
|
||||||
|
},
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
"resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
||||||
@@ -3522,6 +3527,11 @@
|
|||||||
"run-queue": "^1.0.3"
|
"run-queue": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moving-average": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/moving-average/-/moving-average-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-97cgMz0U2zciiDp4xRl/n+MYgrm9l7UiYbtsBLPr0rhw6KH3m4LyK2w4d96V6+UwKo+ph7KtQSoL2qgnqZVgvA=="
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
|||||||
@@ -22,8 +22,10 @@
|
|||||||
"@types/socket.io-client": "^1.4.32",
|
"@types/socket.io-client": "^1.4.32",
|
||||||
"@types/vis": "^4.21.9",
|
"@types/vis": "^4.21.9",
|
||||||
"awesome-typescript-loader": "^5.2.1",
|
"awesome-typescript-loader": "^5.2.1",
|
||||||
|
"copy-text-to-clipboard": "^1.0.4",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"moving-average": "^1.0.0",
|
||||||
"react": "^16.3.2",
|
"react": "^16.3.2",
|
||||||
"react-dom": "^16.3.3",
|
"react-dom": "^16.3.3",
|
||||||
"react-json-view": "^1.19.1",
|
"react-json-view": "^1.19.1",
|
||||||
|
|||||||
@@ -25,15 +25,12 @@ class NodeStats extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const leafes = this.props.node.leafes()
|
const { node } = this.props
|
||||||
const leafMessages = leafes
|
|
||||||
.map(leaf => leaf.messages)
|
|
||||||
.reduce((a, b) => a + b)
|
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<Typography>Messages: #{this.props.node.messages}</Typography>
|
<Typography>Messages: #{node.messages}</Typography>
|
||||||
<Typography>Subtopics: {leafes.length}</Typography>
|
<Typography>Subtopics: {node.leafCount()}</Typography>
|
||||||
<Typography>Messages Subtopics: #{leafMessages}</Typography>
|
<Typography>Messages Subtopics: #{node.leafMessageCount()}</Typography>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as React from 'react'
|
|||||||
import * as q from '../../../../backend/src/Model'
|
import * as q from '../../../../backend/src/Model'
|
||||||
import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles'
|
import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles'
|
||||||
import Button from '@material-ui/core/Button'
|
import Button from '@material-ui/core/Button'
|
||||||
|
const copy = require('copy-text-to-clipboard')
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
classes: any
|
classes: any
|
||||||
@@ -50,7 +51,10 @@ class Topic extends React.Component<Props, {}> {
|
|||||||
prev.concat([<span key={key += 1}>/</span>]).concat(current),
|
prev.concat([<span key={key += 1}>/</span>]).concat(current),
|
||||||
)
|
)
|
||||||
|
|
||||||
return <span style={{ lineHeight: '2.2em' }}>{joinedBreadCrumps}</span>
|
return <span style={{ lineHeight: '2.2em' }}>
|
||||||
|
<a onClick={() => copy(this.props.node && this.props.node.path())}>📋</a>
|
||||||
|
{joinedBreadCrumps}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import * as q from '../../../../backend/src/Model'
|
import * as q from '../../../../backend/src/Model'
|
||||||
import TreeNode from './TreeNode'
|
import TreeNode from './TreeNode'
|
||||||
import List from '@material-ui/core/List'
|
import { Typography } from '@material-ui/core'
|
||||||
import { makeConnectionMessageEvent, rendererEvents } from '../../../../events'
|
import { makeConnectionMessageEvent, rendererEvents } from '../../../../events'
|
||||||
import { } from '../../../../events/Events'
|
import { } from '../../../../events/Events'
|
||||||
|
const MovingAvaerage = require('moving-average')
|
||||||
|
|
||||||
declare const performance: any
|
declare const performance: any
|
||||||
|
|
||||||
|
const timeInterval = 10 * 1000
|
||||||
|
const average = MovingAvaerage(timeInterval)
|
||||||
|
|
||||||
interface Props{
|
interface Props{
|
||||||
didSelectNode?: (node: q.TreeNode) => void
|
didSelectNode?: (node: q.TreeNode) => void
|
||||||
connectionId?: string
|
connectionId?: string
|
||||||
@@ -40,7 +45,8 @@ export class Tree extends React.Component<Props, TreeState> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateInterval = Math.max(this.renderDuration * 5, 300)
|
const expectedRenderTime = average.forecast()
|
||||||
|
const updateInterval = Math.max(expectedRenderTime * 5, 300)
|
||||||
const timeUntilNextUpdate = updateInterval - (performance.now() - this.lastUpdate)
|
const timeUntilNextUpdate = updateInterval - (performance.now() - this.lastUpdate)
|
||||||
|
|
||||||
this.updateTimer = setTimeout(() => {
|
this.updateTimer = setTimeout(() => {
|
||||||
@@ -85,21 +91,20 @@ export class Tree extends React.Component<Props, TreeState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
return <div>
|
return <Typography>
|
||||||
<List>
|
|
||||||
<TreeNode
|
<TreeNode
|
||||||
animateChages={true}
|
animateChages={true}
|
||||||
autoExpandLimit={0}
|
autoExpandLimit={3000}
|
||||||
isRoot={true}
|
isRoot={true}
|
||||||
didSelectNode={this.props.didSelectNode}
|
didSelectNode={this.props.didSelectNode}
|
||||||
treeNode={this.state.tree}
|
treeNode={this.state.tree}
|
||||||
name="/" collapsed={false}
|
name="/" collapsed={false}
|
||||||
key="rootNode"
|
key="rootNode"
|
||||||
performanceCallback={(ms: number) => {
|
performanceCallback={(ms: number) => {
|
||||||
|
average.push(Date.now(), ms)
|
||||||
this.renderDuration = ms
|
this.renderDuration = ms
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</List>
|
</Typography>
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,9 +138,10 @@ class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
|
|||||||
|
|
||||||
this.dirtyState = this.dirtyEdges = this.dirtyMessage = this.dirtySubnodes = false
|
this.dirtyState = this.dirtyEdges = this.dirtyMessage = this.dirtySubnodes = false
|
||||||
|
|
||||||
return <div key={this.props.treeNode.hash()} style={ displayBlock }>
|
return <div key={this.props.treeNode.hash()} style={ { display: 'block', marginLeft: '10px' } }>
|
||||||
<div style={animationStyle} ref={this.titleRef} onClick={() => this.toggle()}>
|
<span ref={this.titleRef} style={animationStyle}>
|
||||||
<TreeNodeTitle
|
<TreeNodeTitle
|
||||||
|
onClick={() => this.toggle()}
|
||||||
edgeCount={this.state.edgeCount}
|
edgeCount={this.state.edgeCount}
|
||||||
collapsed={this.collapsed()}
|
collapsed={this.collapsed()}
|
||||||
treeNode={this.props.treeNode}
|
treeNode={this.props.treeNode}
|
||||||
@@ -148,17 +149,9 @@ class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
|
|||||||
didSelectNode={this.props.didSelectNode}
|
didSelectNode={this.props.didSelectNode}
|
||||||
toggleCollapsed={() => this.toggle()}
|
toggleCollapsed={() => this.toggle()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</span>
|
||||||
|
|
||||||
{ this.clear() }
|
|
||||||
<div style = { displayBlock }>
|
|
||||||
{ this.renderNodes() }
|
{ this.renderNodes() }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
private clear() {
|
|
||||||
return <div style={{ clear: 'both' }} />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderNodes() {
|
private renderNodes() {
|
||||||
@@ -172,7 +165,7 @@ class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
private indicatingChangeAnimationStyle() {
|
private indicatingChangeAnimationStyle(): React.CSSProperties {
|
||||||
if (this.props.isRoot) {
|
if (this.props.isRoot) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
@@ -188,6 +181,8 @@ class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
|
|||||||
}
|
}
|
||||||
return { animation: 'example 0.5s' }
|
return { animation: 'example 0.5s' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ class TreeNodeSubnodes extends React.Component<Props, {}> {
|
|||||||
const listItems = edges
|
const listItems = edges
|
||||||
.map(edge => edge.target)
|
.map(edge => edge.target)
|
||||||
.map(node => (
|
.map(node => (
|
||||||
<ListItem
|
<div
|
||||||
key={node.hash()}
|
key={node.hash()}
|
||||||
style={listItemStyle}
|
style={listItemStyle}
|
||||||
button
|
|
||||||
>
|
>
|
||||||
<TreeNode
|
<TreeNode
|
||||||
animateChages={this.props.animateChanges}
|
animateChages={this.props.animateChanges}
|
||||||
@@ -40,12 +39,14 @@ class TreeNodeSubnodes extends React.Component<Props, {}> {
|
|||||||
didSelectNode={this.props.didSelectNode}
|
didSelectNode={this.props.didSelectNode}
|
||||||
autoExpandLimit={this.props.autoExpandLimit}
|
autoExpandLimit={this.props.autoExpandLimit}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</div>
|
||||||
))
|
))
|
||||||
|
|
||||||
return <Collapse in={!this.props.collapsed} timeout="auto" unmountOnExit>
|
return <span
|
||||||
<List style={listStyle}>{listItems}</List>
|
style={{ display: 'block', clear: 'both' }}
|
||||||
</Collapse>
|
>
|
||||||
|
{this.props.collapsed ? null : listItems}
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import * as q from '../../../../backend/src/Model'
|
|||||||
import { Typography } from '@material-ui/core'
|
import { Typography } from '@material-ui/core'
|
||||||
import { withTheme, Theme } from '@material-ui/core/styles'
|
import { withTheme, Theme } from '@material-ui/core/styles'
|
||||||
|
|
||||||
export interface TreeNodeProps {
|
export interface TreeNodeProps extends React.HTMLAttributes<HTMLElement> {
|
||||||
treeNode: q.TreeNode
|
treeNode: q.TreeNode
|
||||||
|
// ref: React.Ref<HTMLElement>
|
||||||
name?: string | undefined
|
name?: string | undefined
|
||||||
collapsed?: boolean | undefined
|
collapsed?: boolean | undefined
|
||||||
toggleCollapsed: () => void
|
toggleCollapsed: () => void
|
||||||
@@ -31,9 +32,14 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
|||||||
lineHeight: '1em',
|
lineHeight: '1em',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
}
|
}
|
||||||
return <Typography style={style}>
|
return <span
|
||||||
|
style={style}
|
||||||
|
onClick={() => {
|
||||||
|
this.toggle()
|
||||||
|
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
|
||||||
|
}}>
|
||||||
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
|
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
|
||||||
</Typography>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSourceEdge() {
|
private renderSourceEdge() {
|
||||||
@@ -44,10 +50,7 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
|||||||
}
|
}
|
||||||
const name = this.props.name || (this.props.treeNode.sourceEdge && this.props.treeNode.sourceEdge.name)
|
const name = this.props.name || (this.props.treeNode.sourceEdge && this.props.treeNode.sourceEdge.name)
|
||||||
|
|
||||||
return <span style={style} onClick={() => {
|
return <span style={style}>{name}</span>
|
||||||
this.toggle()
|
|
||||||
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
|
|
||||||
}}>{name}</span>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderValue() {
|
private renderValue() {
|
||||||
@@ -77,9 +80,7 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.props.collapsed
|
return this.props.collapsed ? '▶' : '▼'
|
||||||
? <span onClick={() => this.toggle()}>▶</span>
|
|
||||||
: <span onClick={() => this.toggle()}>▼</span>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderCollapsedSubnodes() {
|
private renderCollapsedSubnodes() {
|
||||||
@@ -87,8 +88,8 @@ class TreeNodeTitle extends React.Component<TreeNodeProps, {}> {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = this.props.treeNode.leafes().map(leaf => leaf.messages).reduce((a, b) => a + b)
|
const messages = this.props.treeNode.leafMessageCount()
|
||||||
return <span style={this.getStyles().collapsedSubnodes}>({this.props.treeNode.leafes().length} nodes, {messages} messages)</span>
|
return <span style={this.getStyles().collapsedSubnodes}>({this.props.treeNode.leafCount()} nodes, {messages} messages)</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export class TreeNode {
|
|||||||
public onMerge = new EventDispatcher<void, TreeNode>(this)
|
public onMerge = new EventDispatcher<void, TreeNode>(this)
|
||||||
public onEdgesChange = new EventDispatcher<void, TreeNode>(this)
|
public onEdgesChange = new EventDispatcher<void, TreeNode>(this)
|
||||||
public onMessage = new EventDispatcher<Message, TreeNode>(this)
|
public onMessage = new EventDispatcher<Message, TreeNode>(this)
|
||||||
|
private cachedLeafes?: TreeNode[]
|
||||||
|
private cachedLeafMessageCount?: number
|
||||||
|
|
||||||
constructor(sourceEdge?: Edge, message?: Message) {
|
constructor(sourceEdge?: Edge, message?: Message) {
|
||||||
if (sourceEdge) {
|
if (sourceEdge) {
|
||||||
@@ -19,6 +21,10 @@ export class TreeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setMessage(message)
|
this.setMessage(message)
|
||||||
|
this.onMerge.subscribe(() => {
|
||||||
|
this.cachedLeafes = undefined
|
||||||
|
this.cachedLeafMessageCount = undefined
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public setMessage(value: any) {
|
public setMessage(value: any) {
|
||||||
@@ -73,16 +79,34 @@ export class TreeNode {
|
|||||||
this.onMerge.dispatch()
|
this.onMerge.dispatch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public leafMessageCount() {
|
||||||
|
if (this.cachedLeafMessageCount === undefined) {
|
||||||
|
this.cachedLeafMessageCount = this.leafes()
|
||||||
|
.map(leaf => leaf.messages)
|
||||||
|
.reduce((a, b) => a + b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.cachedLeafMessageCount
|
||||||
|
}
|
||||||
|
|
||||||
|
public leafCount(): number {
|
||||||
|
return this.leafes().length
|
||||||
|
}
|
||||||
|
|
||||||
public leafes(): TreeNode[] {
|
public leafes(): TreeNode[] {
|
||||||
|
if (this.cachedLeafes === undefined) {
|
||||||
if (Object.values(this.edges).length === 0) {
|
if (Object.values(this.edges).length === 0) {
|
||||||
return [this]
|
return [this]
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.values(this.edges)
|
this.cachedLeafes = Object.values(this.edges)
|
||||||
.map(e => e.target.leafes())
|
.map(e => e.target.leafes())
|
||||||
.reduce((a, b) => a.concat(b), [])
|
.reduce((a, b) => a.concat(b), [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.cachedLeafes
|
||||||
|
}
|
||||||
|
|
||||||
private mergeEdges(node: TreeNode) {
|
private mergeEdges(node: TreeNode) {
|
||||||
const edgeKeys = Object.keys(node.edges)
|
const edgeKeys = Object.keys(node.edges)
|
||||||
let edgesDidUpdate = false
|
let edgesDidUpdate = false
|
||||||
|
|||||||
Reference in New Issue
Block a user