chore: refactor

This commit is contained in:
Thomas Nordquist
2024-05-22 14:44:06 +02:00
parent 1ecb53b397
commit b3a37e4794
20 changed files with 1037 additions and 524 deletions

View File

@@ -12,7 +12,6 @@ import { sidebarActions } from '../../../actions'
const TopicPanel = (props: { node?: q.TreeNode<any>; actions: typeof sidebarActions }) => {
const { node } = props
console.log(node && node.path())
const copyTopic = node ? <Copy value={node.path()} /> : null
@@ -35,7 +34,7 @@ const TopicPanel = (props: { node?: q.TreeNode<any>; actions: typeof sidebarActi
<Topic node={node} />
</Panel>
),
[node, node && node.childTopicCount()]
[node, node?.childTopicCount()]
)
}

View File

@@ -11,23 +11,6 @@ import WarningRounded from '@material-ui/icons/WarningRounded'
import { IDecoder, decoders } from '../../../../../backend/src/Model/sparkplugb'
import { Tooltip } from '@material-ui/core'
// const options: q.TopicDataType[] = ['json', 'string', 'hex', 'integer', 'unsigned int', 'floating point']
const options: q.TopicDataType[] = [
'json',
'string',
'hex',
'uint8',
'uint16',
'uint32',
'uint64',
'int8',
'int16',
'int32',
'int64',
'float',
'double',
]
export const TopicTypeButton = (props: { node?: q.TreeNode<any> }) => {
const { node } = props
if (!node || !node.message || !node.message.payload) {
@@ -39,34 +22,40 @@ export const TopicTypeButton = (props: { node?: q.TreeNode<any> }) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
const [open, setOpen] = React.useState(false)
const selectOption = useCallback((decoder: IDecoder, format: string) => {
if (!node) {
return
}
node.decoder = decoder
node.decoderFormat = format
setOpen(false)
}, [])
const selectOption = useCallback(
(decoder: IDecoder, format: string) => {
if (!node) {
return
}
const handleToggle = (event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation()
if (open === true) {
return
}
setAnchorEl(event.currentTarget)
setOpen(prevOpen => !prevOpen)
}
node.viewModel.decoder = { decoder, format }
setOpen(false)
},
[node]
)
const handleClose = (event: React.MouseEvent<Document, MouseEvent>) => {
const handleToggle = useCallback(
(event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation()
if (open === true) {
return
}
setAnchorEl(event.currentTarget)
setOpen(prevOpen => !prevOpen)
},
[open]
)
const handleClose = useCallback((event: React.MouseEvent<Document, MouseEvent>) => {
if (anchorEl && anchorEl.contains(event.target as HTMLElement)) {
return
}
setOpen(false)
}
}, [])
return (
<Button onClick={handleToggle}>
{props.node?.decoderFormat ?? props.node?.type}
{props.node?.viewModel.decoder?.format ?? props.node?.type}
<Popper open={open} anchorEl={anchorEl} role={undefined} transition>
{({ TransitionProps, placement }) => (
<Grow

View File

@@ -5,7 +5,6 @@ import Copy from '../../helper/Copy'
import DateFormatter from '../../helper/DateFormatter'
import History from '../HistoryDrawer'
import TopicPlot from '../../TopicPlot'
import { Base64Message } from '../../../../../backend/src/Model/Base64Message'
import { isPlottable } from '../CodeDiff/util'
import { TopicViewModel } from '../../../model/TopicViewModel'
import { bindActionCreators } from 'redux'
@@ -13,6 +12,8 @@ import { chartActions } from '../../../actions'
import { connect } from 'react-redux'
import CustomIconButton from '../../helper/CustomIconButton'
import { MessageId } from '../MessageId'
import { useSubscription } from '../../hooks/useSubscription'
import { useDecoder } from '../../hooks/useDecoder'
const throttle = require('lodash.throttle')
@@ -25,119 +26,100 @@ interface Props {
}
}
interface State {
displayMessage?: q.Message
anchorEl?: HTMLElement
lastUpdate: number
}
export const MessageHistory: React.FC<Props> = props => {
const [, setLastUpdate] = React.useState(Date.now())
const updateNodeThrottled = React.useCallback(
throttle(() => {
setLastUpdate
}, 300),
[]
)
class MessageHistory extends React.PureComponent<Props, State> {
private updateNode = throttle(() => {
this.setState({ lastUpdate: Date.now() })
}, 300)
useSubscription(props.node?.onMessage, updateNodeThrottled)
const decodeMessage = useDecoder(props.node)
constructor(props: any) {
super(props)
this.state = { lastUpdate: 0 }
}
private addNodeToCharts = (event: React.MouseEvent) => {
function addNodeToCharts(event: React.MouseEvent) {
event.preventDefault()
event.stopPropagation()
const { node } = this.props
const { node } = props
if (!node) {
return null
}
this.props.actions.charts.addChart({ topic: node.path() })
props.actions.charts.addChart({ topic: node.path() })
}
private displayMessage = (index: number, eventTarget: EventTarget) => {
const message = this.props.node && this.props.node.messageHistory.toArray().reverse()[index]
function displayMessage(index: number, eventTarget: EventTarget) {
const message = props.node && props.node.messageHistory.toArray().reverse()[index]
if (message) {
this.props.onSelect(message)
props.onSelect(message)
}
}
public componentWillReceiveProps(nextProps: Props) {
this.props.node && this.props.node.onMessage.unsubscribe(this.updateNode)
nextProps.node && nextProps.node.onMessage.subscribe(this.updateNode)
const { node } = props
if (!node) {
return null
}
public componentDidMount() {
this.props.node && this.props.node.onMessage.subscribe(this.updateNode)
}
const history = node.messageHistory.toArray()
let previousMessage: q.Message | undefined = node.message
const historyElements = [...history].reverse().map((message, idx) => {
const value = node.message ? decodeMessage(node.message)?.format()[0] ?? null : null
public componentWillUnMount() {
this.props.node && this.props.node.onMessage.unsubscribe(this.updateNode)
}
public render() {
const { node } = this.props
if (!node) {
return null
}
const history = node.messageHistory.toArray()
let previousMessage: q.Message | undefined = node.message
const historyElements = [...history].reverse().map((message, idx) => {
const value = node.message ? node.decodeMessage(node.message)?.format()[0] ?? null : null
const element = {
value: value ?? '',
key: `${message.messageNumber}-${message.received}`,
title: (
const element = {
value: value ?? '',
key: `${message.messageNumber}-${message.received}`,
title: (
<span>
<div style={{ float: 'left' }}>
<DateFormatter date={message.received} />
{previousMessage && previousMessage !== message ? (
<i>
(-
<DateFormatter date={message.received} intervalSince={previousMessage.received} />)
</i>
) : null}
</div>
<span>
<div style={{ float: 'left' }}>
<DateFormatter date={message.received} />
{previousMessage && previousMessage !== message ? (
<i>
(-
<DateFormatter date={message.received} intervalSince={previousMessage.received} />)
</i>
) : null}
</div>
<span>
&nbsp;
<MessageId message={message} />
</span>
<div style={{ float: 'right' }}>
<Copy value={value ?? ''} />
</div>
&nbsp;
<MessageId message={message} />
</span>
),
selected: message && message === this.props.selected,
}
previousMessage = message
return element
})
<div style={{ float: 'right' }}>
<Copy value={value ?? ''} />
</div>
</span>
),
selected: message && message === props.selected,
}
previousMessage = message
return element
})
const value = node.message ? node.decodeMessage(node.message)?.format()[0] ?? null : null
const value = node.message ? decodeMessage(node.message)?.format()[0] ?? null : null
const isMessagePlottable = isPlottable(value)
return (
<div>
<History
items={historyElements}
contentTypeIndicator={
isMessagePlottable ? (
<CustomIconButton
style={{ height: '22px', width: '22px' }}
onClick={this.addNodeToCharts}
tooltip="Add to chart panel"
>
<ShowChart style={{ marginTop: '-5px' }} />
</CustomIconButton>
) : undefined
}
onClick={this.displayMessage}
>
{isMessagePlottable ? <TopicPlot node={node} history={node.messageHistory} /> : null}
</History>
</div>
)
}
const isMessagePlottable = isPlottable(value)
return (
<div>
<History
items={historyElements}
contentTypeIndicator={
isMessagePlottable ? (
<CustomIconButton
style={{ height: '22px', width: '22px' }}
onClick={addNodeToCharts}
tooltip="Add to chart panel"
>
<ShowChart style={{ marginTop: '-5px' }} />
</CustomIconButton>
) : undefined
}
onClick={displayMessage}
>
{isMessagePlottable ? <TopicPlot node={node} history={node.messageHistory} /> : null}
</History>
</div>
)
}
const mapDispatchToProps = (dispatch: any) => {
@@ -146,4 +128,4 @@ const mapDispatchToProps = (dispatch: any) => {
}
}
export default connect(null, mapDispatchToProps)(MessageHistory)
export default connect(null, mapDispatchToProps)(React.memo(MessageHistory))

View File

@@ -7,13 +7,13 @@ import Panel from '../Panel'
import React, { useCallback } from 'react'
import ValueRenderer from './ValueRenderer'
import { AppState } from '../../../reducers'
import { Base64Message } from '../../../../../backend/src/Model/Base64Message'
import { bindActionCreators } from 'redux'
import { Theme, Typography, withStyles } from '@material-ui/core'
import { connect } from 'react-redux'
import { sidebarActions } from '../../../actions'
import DeleteSelectedTopicButton from './DeleteSelectedTopicButton'
import { MessageId } from '../MessageId'
import { useDecoder } from '../../hooks/useDecoder'
interface Props {
node?: q.TreeNode<any>
@@ -35,6 +35,7 @@ function RenderedValue(props: { node?: q.TreeNode<any>; compareMessage?: q.Messa
function ValuePanel(props: Props) {
const { node, compareMessage } = props
const decodeMessage = useDecoder(node)
function renderViewOptions() {
if (!props.node || !props.node.message) {
@@ -54,6 +55,10 @@ function ValuePanel(props: Props) {
)
}
const getDecodedValue = useCallback(() => {
return node?.message && decodeMessage(node.message)?.toUnicodeString()
}, [node, decodeMessage])
function messageMetaInfo() {
if (!props.node || !props.node.message) {
return null
@@ -87,7 +92,7 @@ function ValuePanel(props: Props) {
const [value] =
node && node.message && node.message.payload ? node.message.payload?.format(node.type) : [null, undefined]
const copyValue = value ? <Copy value={value} /> : null
const copyValue = value ? <Copy getValue={getDecodedValue} /> : null
return (
<Panel>

View File

@@ -6,6 +6,7 @@ import { connect } from 'react-redux'
import { ValueRendererDisplayMode } from '../../../reducers/Settings'
import { Fade } from '@material-ui/core'
import { Decoder } from '../../../../../backend/src/Model/Decoder'
import { DecoderFunction, useDecoder } from '../../hooks/useDecoder'
interface Props {
message: q.Message
@@ -14,30 +15,28 @@ interface Props {
renderMode: ValueRendererDisplayMode
}
interface State {
width: number
}
export const ValueRenderer: React.FC<Props> = props => {
const decodeMessage = useDecoder(props.treeNode)
class ValueRenderer extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { width: 0 }
}
private renderDiff(current: string = '', previous: string = '', title?: string, language?: 'json') {
function renderDiff(current: string = '', previous: string = '', title?: string, language?: 'json') {
return (
<CodeDiff
treeNode={this.props.treeNode}
treeNode={props.treeNode}
previous={previous}
current={current}
title={title}
language={language}
nameOfCompareMessage={this.props.compareWith ? 'selected' : 'previous'}
nameOfCompareMessage={props.compareWith ? 'selected' : 'previous'}
/>
)
}
private renderDiffMode(message: q.Message, treeNode: q.TreeNode<any>, compare?: q.Message) {
function renderDiffMode(
decodeMessage: DecoderFunction,
message: q.Message,
treeNode: q.TreeNode<any>,
compare?: q.Message
) {
if (!message.payload) {
return
}
@@ -46,55 +45,58 @@ class ValueRenderer extends React.Component<Props, State> {
const previousMessage = previousMessages[previousMessages.length - 2]
const compareMessage = compare || previousMessage || message
const [currentStr, currentType] = treeNode.decodeMessage(message)?.format(treeNode.type) ?? []
const [compareStr, compareType] = treeNode.decodeMessage(compareMessage)?.format(treeNode.type) ?? []
const [currentStr, currentType] = decodeMessage(message)?.format(treeNode.type) ?? []
const [compareStr, compareType] = decodeMessage(compareMessage)?.format(treeNode.type) ?? []
const language = currentType === compareType && compareType === 'json' ? 'json' : undefined
return <div>{this.renderDiff(currentStr, compareStr, undefined, language)}</div>
return <div>{renderDiff(currentStr, compareStr, undefined, language)}</div>
}
private renderRawMode(message: q.Message, treeNode: q.TreeNode<any>, compare?: q.Message) {
function renderRawMode(
decodeMessage: DecoderFunction,
message: q.Message,
treeNode: q.TreeNode<any>,
compare?: q.Message
) {
if (!message.payload) {
return
}
const [currentStr, currentType] = treeNode.decodeMessage(message)?.format(treeNode.type) ?? []
const [currentStr, currentType] = decodeMessage(message)?.format(treeNode.type) ?? []
const [compareStr, compareType] =
compare && compare.payload ? treeNode.decodeMessage(compare)?.format(treeNode.type) ?? [] : []
compare && compare.payload ? decodeMessage(compare)?.format(treeNode.type) ?? [] : []
return (
<div>
{this.renderDiff(currentStr, currentStr, undefined, currentType)}
{renderDiff(currentStr, currentStr, undefined, currentType)}
<Fade in={Boolean(compareStr)} timeout={400}>
<div>{Boolean(compareStr) ? this.renderDiff(compareStr, compareStr, 'selected', compareType) : null}</div>
<div>{Boolean(compareStr) ? renderDiff(compareStr, compareStr, 'selected', compareType) : null}</div>
</Fade>
</div>
)
}
public render() {
return (
<div style={{ padding: '0px 0px 8px 0px', width: '100%' }}>
{this.props.message?.payload?.decoder === Decoder.SPARKPLUG && 'Decoded SparkplugB'}
{this.renderValue()}
</div>
)
}
public renderValue() {
const { message, treeNode, compareWith, renderMode } = this.props
function renderValue(decodeMessage: DecoderFunction) {
const { message, treeNode, compareWith, renderMode } = props
if (!message.payload) {
return null
}
switch (renderMode) {
case 'diff':
return this.renderDiffMode(message, treeNode, compareWith)
return renderDiffMode(decodeMessage, message, treeNode, compareWith)
default:
return this.renderRawMode(message, treeNode, compareWith)
return renderRawMode(decodeMessage, message, treeNode, compareWith)
}
}
return (
<div style={{ padding: '0px 0px 8px 0px', width: '100%' }}>
{props.message?.payload?.decoder === Decoder.SPARKPLUG && 'Decoded SparkplugB'}
{renderValue(decodeMessage)}
</div>
)
}
const mapStateToProps = (state: AppState) => {