This commit is contained in:
Thomas Nordquist
2019-07-11 15:33:42 +02:00
parent df42d75651
commit 8e49d19fbe
10 changed files with 84 additions and 48 deletions

View File

@@ -28,6 +28,7 @@
"file-loader": "^4.0.0",
"get-value": "^3.0.1",
"immutable": "^4.0.0-rc.12",
"in-viewport": "^3.6.0",
"js-base64": "^2.5.1",
"json-to-ast": "^2.1.0",
"lodash.debounce": "^4.0.8",

View File

@@ -99,13 +99,16 @@ export const togglePause = (tree?: q.Tree<TopicViewModel>) => (dispatch: Dispatc
const tree = getState().tree.get('tree')
const changes = tree ? tree.unmergedChanges().length : 0
if (tree) {
paused ? tree.resume() : tree.pause()
}
if (paused && changes > 0) {
dispatch(globalActions.showNotification('Applying recorded changes.'))
}
// Allow for notification to be displayed
setTimeout(() => {
tree && tree.applyUnmergedChanges()
if (paused && changes > 0) {
dispatch(globalActions.showNotification(`Successfully applied ${changes} changes.`))
}

View File

@@ -29,7 +29,7 @@ function useStagedRendering(treeNode: q.TreeNode<any>) {
if (alreadyAdded < edges.length) {
renderMoreAnimationFrame = (window as any).requestIdleCallback(
() => {
setAlreadyAdded(alreadyAdded * 1.5)
setAlreadyAdded(Math.max(25, alreadyAdded * 1.5))
},
{ timeout: 500 }
)

View File

@@ -1,4 +1,5 @@
import React, { useEffect } from 'react'
var inViewport = require('in-viewport')
export function useAnimationToIndicateTopicUpdate(
ref: React.MutableRefObject<HTMLDivElement | undefined>,
@@ -9,6 +10,10 @@ export function useAnimationToIndicateTopicUpdate(
) {
useEffect(() => {
if (ref.current && shouldAnimate && Date.now() - lastUpdate < 3000 && !selected) {
if (!inViewport(ref.current)) {
return
}
let timeout: any
let animationFrame = requestAnimationFrame(() => {
ref.current && ref.current.classList.add(className)

View File

@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'
export function useIsAllowedToAutoExpandState(props: Props): boolean {
const { settings, treeNode, isRoot } = props
const [isAllowedToAutoExpand, setAllowAutoExpand] = useState(false)
useEffect(() => {
const newIsAllowedToAutoExpand = isRoot || treeNode.edgeCount() <= settings.get('autoExpandLimit')
if (newIsAllowedToAutoExpand !== isAllowedToAutoExpand) {

View File

@@ -8,37 +8,37 @@ export function useViewModelSubscriptions(
setSelected: (value: boolean) => void,
setCollapsedOverride: (value: boolean) => void
) {
const selectionDidChange = () => {
const selected = treeNode.viewModel && treeNode.viewModel.isSelected()
treeNode.viewModel && setSelected(Boolean(selected))
if (selected && nodeRef && nodeRef.current) {
nodeRef.current.focus({ preventScroll: false })
}
}
const expandedDidChange = () => {
treeNode.viewModel && setCollapsedOverride(!treeNode.viewModel.isExpanded())
}
useEffect(() => {
const selectionDidChange = () => {
const selected = treeNode.viewModel && treeNode.viewModel.isSelected()
treeNode.viewModel && setSelected(Boolean(selected))
if (selected && nodeRef && nodeRef.current) {
nodeRef.current.focus({ preventScroll: false })
}
}
const expandedDidChange = () => {
treeNode.viewModel && setCollapsedOverride(!treeNode.viewModel.isExpanded())
}
function addSubscriber() {
treeNode.viewModel = new TopicViewModel()
treeNode.viewModel.selectionChange.subscribe(selectionDidChange)
treeNode.viewModel.expandedChange.subscribe(expandedDidChange)
}
function removeSubscriber() {
if (treeNode.viewModel) {
treeNode.viewModel.selectionChange.unsubscribe(selectionDidChange)
treeNode.viewModel.expandedChange.unsubscribe(expandedDidChange)
treeNode.viewModel = undefined
}
}
addSubscriber()
return function cleanup() {
removeSubscriber()
}
}, [treeNode])
function addSubscriber() {
treeNode.viewModel = new TopicViewModel()
treeNode.viewModel.selectionChange.subscribe(selectionDidChange)
treeNode.viewModel.expandedChange.subscribe(expandedDidChange)
}
function removeSubscriber() {
if (treeNode.viewModel) {
treeNode.viewModel.selectionChange.unsubscribe(selectionDidChange)
treeNode.viewModel.expandedChange.unsubscribe(expandedDidChange)
treeNode.viewModel = undefined
}
}
}

View File

@@ -48,10 +48,6 @@ function TreeNodeComponent(props: Props) {
const isCollapsed =
Boolean(collapsedOverride) === collapsedOverride ? Boolean(collapsedOverride) : !isAllowedToAutoExpand
const toggle = useCallback(() => {
setCollapsedOverride(!isCollapsed)
}, [isCollapsed])
const didSelectTopic = useCallback(
(event?: React.MouseEvent) => {
event && event.stopPropagation()
@@ -64,17 +60,17 @@ function TreeNodeComponent(props: Props) {
(event: React.MouseEvent) => {
event.stopPropagation()
didSelectTopic()
toggle()
setCollapsedOverride(!isCollapsed)
},
[toggle, didSelectTopic]
[isCollapsed, didSelectTopic]
)
const toggleCollapsed = useCallback(
(event: React.MouseEvent) => {
event.stopPropagation()
toggle()
setCollapsedOverride(!isCollapsed)
},
[toggle]
[isCollapsed]
)
const didObtainFocus = useCallback(() => {

View File

@@ -4,11 +4,10 @@ import TreeNode from './TreeNode'
import { AppState } from '../../reducers'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { KeyCodes } from '../../utils/KeyCodes'
import { SettingsState } from '../../reducers/Settings'
import { TopicViewModel } from '../../model/TopicViewModel'
import { treeActions } from '../../actions'
import { KeyCodes } from '../../utils/KeyCodes'
const MovingAverage = require('moving-average')
const averagingTimeInterval = 10 * 1000
@@ -70,17 +69,17 @@ class TreeComponent extends React.PureComponent<Props, State> {
public componentWillReceiveProps(nextProps: Props) {
if (this.props.tree !== nextProps.tree) {
if (this.props.tree) {
this.props.tree.didReceive.unsubscribe(this.throttledTreeUpdate)
this.props.tree.didUpdate.unsubscribe(this.throttledTreeUpdate)
}
if (nextProps.tree) {
nextProps.tree.didReceive.subscribe(this.throttledTreeUpdate)
nextProps.tree.didUpdate.subscribe(this.throttledTreeUpdate)
}
this.setState(this.state)
}
}
public componentWillUnmount() {
this.props.tree && this.props.tree.didReceive.unsubscribe(this.throttledTreeUpdate)
this.props.tree && this.props.tree.didUpdate.unsubscribe(this.throttledTreeUpdate)
}
public throttledTreeUpdate = () => {
@@ -99,9 +98,6 @@ class TreeComponent extends React.PureComponent<Props, State> {
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 })

View File

@@ -3038,6 +3038,11 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
in-viewport@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/in-viewport/-/in-viewport-3.6.0.tgz#c59b4cdcaa41adb5bf5b8fe390c7d34259891f4a"
integrity sha512-MhaJ7Pr3NhUyAfpULysTZZBUAYfJAX1O8PccW2gvXlbQduMrJz7qQQ5yzC7SAr/0g5LbeRk432yNjsLMCnYzJg==
indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"

View File

@@ -12,7 +12,11 @@ export class Tree<ViewModel extends Destroyable> extends TreeNode<ViewModel> {
public isTree = true
private cachedHash = `${Math.random()}`
private unmergedMessages: ChangeBuffer = new ChangeBuffer()
public didReceive = new EventDispatcher<void>()
public didUpdate = new EventDispatcher<void>()
public updateInterval: any
private paused: boolean = false
private applyChangesHasCompleted = true
constructor() {
super(undefined, undefined)
@@ -20,14 +24,27 @@ export class Tree<ViewModel extends Destroyable> extends TreeNode<ViewModel> {
private handleNewData = (msg: MqttMessage) => {
this.unmergedMessages.push(msg)
this.didReceive.dispatch()
}
private runUpdates() {
this.updateInterval = setInterval(() => {
if (!this.paused && this.applyChangesHasCompleted) {
this.applyChangesHasCompleted = false
if ((window as any).requestIdleCallback) {
;(window as any).requestIdleCallback(() => this.applyUnmergedChanges(), { timeout: 500 })
} else {
this.applyUnmergedChanges()
}
}
}, 300)
}
public destroy() {
super.destroy()
this.updateInterval && clearInterval(this.updateInterval)
this.updateSource && this.updateSource.unsubscribe(this.subscriptionEvent, this.handleNewData)
this.updateSource = undefined
this.didReceive.removeAllListeners()
this.didUpdate.removeAllListeners()
}
public updateWithConnection(
@@ -41,12 +58,21 @@ export class Tree<ViewModel extends Destroyable> extends TreeNode<ViewModel> {
this.subscriptionEvent = makeConnectionMessageEvent(connectionId)
this.updateSource.subscribe(this.subscriptionEvent, this.handleNewData)
this.runUpdates()
}
public hash() {
return this.cachedHash
}
public pause() {
this.paused = true
}
public resume() {
this.paused = false
}
public applyUnmergedChanges() {
this.unmergedMessages.popAll().forEach(bufferedItem => {
const edges = bufferedItem.message.topic.split('/')
@@ -61,6 +87,9 @@ export class Tree<ViewModel extends Destroyable> extends TreeNode<ViewModel> {
this.updateWithNode(node.firstNode())
}
})
this.didUpdate.dispatch()
this.applyChangesHasCompleted = true
}
public unmergedChanges(): ChangeBuffer {