diff --git a/images/firefly_explorer.png b/images/firefly_explorer.png index 06cc36c4..8d8ae2a3 100644 Binary files a/images/firefly_explorer.png and b/images/firefly_explorer.png differ diff --git a/src/components/Charts/DiagramFireFlyNode.tsx b/src/components/Charts/DiagramFireFlyNode.tsx index 2b24a4b5..5edea0e5 100644 --- a/src/components/Charts/DiagramFireFlyNode.tsx +++ b/src/components/Charts/DiagramFireFlyNode.tsx @@ -1,20 +1,36 @@ -import { Grid, Typography } from '@mui/material'; +import { Box, Grid, styled, Typography } from '@mui/material'; import { memo } from 'react'; import { Handle, Position } from 'react-flow-renderer'; -import { IStatus, IWebsocketConnection } from '../../interfaces'; +import { IStatus } from '../../interfaces'; import { APP_PREFIX, HANDLE_PREFIX, PLUGIN_PREFIX } from './MyNodeDiagram'; +import { ReactComponent as LogoSVG } from '../..//svg/ff-logo-symbol-white.svg'; +import i18next from 'i18next'; + +export const FFLogo: React.FC = () => { + const StyledLogo = styled(LogoSVG)({ + width: 60, + }); + return ( + + + + ); +}; interface FFNodeProps { data: { - applications: IWebsocketConnection[]; + applications: IStatus['plugins']; plugins: IStatus['plugins']; - label: string; - subtitle: string; + nodeName: string; }; } export const DiagramFireFlyNode = memo((children: FFNodeProps) => { if ( - children.data.applications?.length && + Object.keys(children.data.applications)?.length && Object.keys(children.data.plugins)?.length ) { return ( @@ -28,41 +44,53 @@ export const DiagramFireFlyNode = memo((children: FFNodeProps) => { width={'100%'} > - {children.data.label} + {i18next.t('firefly')} + + + {i18next.t('core')} + - {children.data.subtitle} + {children.data.nodeName} - {children.data.applications.map((_, idx) => { - return ( - - ); + {Object.keys(children.data.applications).map((k, idx) => { + // Only show events on left side + if (k === 'events') { + return ( + + ); + } })} - {Object.keys(children.data.plugins).map((_, idx) => { - return ( - - ); + {Object.keys(children.data.plugins).map((k, idx) => { + if (k !== 'events') { + return ( + + ); + } })} ); diff --git a/src/components/Charts/MyNodeDiagram.tsx b/src/components/Charts/MyNodeDiagram.tsx index acde5301..98e2417f 100644 --- a/src/components/Charts/MyNodeDiagram.tsx +++ b/src/components/Charts/MyNodeDiagram.tsx @@ -5,7 +5,6 @@ import i18next from 'i18next'; import React, { useContext, useEffect, useState } from 'react'; import ReactFlow, { Edge, - MarkerType, Node, NodeTypes, Position, @@ -14,7 +13,7 @@ import ReactFlow, { useNodesState, } from 'react-flow-renderer'; import { ApplicationContext } from '../../contexts/ApplicationContext'; -import { IStatus, IWebsocketConnection } from '../../interfaces'; +import { IStatus } from '../../interfaces'; import { DEFAULT_BORDER_RADIUS, FFColors } from '../../theme'; import { FFCircleLoader } from '../Loaders/FFCircleLoader'; import { DiagramFireFlyNode } from './DiagramFireFlyNode'; @@ -45,10 +44,13 @@ const edgeStyle = { const dagreGraph = new dagre.graphlib.Graph(); dagreGraph.setDefaultEdgeLabel(() => ({})); -const nodeWidth = 350; -const nodeHeight = 25; - -const getLayoutedElements = (nodes: Node[], edges: Edge[]) => { +const getLayoutedElements = ( + nodes: Node[], + edges: Edge[], + isSmall: boolean +) => { + const nodeWidth = isSmall ? 175 : 350; + const nodeHeight = 25; dagreGraph.setGraph({ rankdir: 'LR' }); nodes.forEach((node) => { @@ -86,24 +88,29 @@ const getLayoutedElements = (nodes: Node[], edges: Edge[]) => { }; const makeInitialNodes = ( - applications: any[], + applications: IStatus['plugins'], plugins: IStatus['plugins'], nodeName: string ) => { let nodes: Node[] = []; // Applications (Left side) - nodes = nodes.concat( - applications.map((a, idx) => { - return { - id: `${APP_PREFIX}${idx}`, - sourcePosition: Position.Right, - type: 'input', - style: nodeStyle, - data: { label: a.remoteAddress }, - position, - }; - }) - ); + Object.entries(applications).map(([k, v]) => { + // Only show events on left side + if (k === 'events') { + nodes = nodes.concat( + v.map((plugin, idx) => { + return { + id: `${APP_PREFIX}${k}_${idx}`, + sourcePosition: Position.Right, + type: 'input', + style: nodeStyle, + data: { label: plugin.name ? plugin.name : plugin.pluginType }, + position, + }; + }) + ); + } + }); // Firefly nodes.push({ type: 'fireflyNode', @@ -114,85 +121,104 @@ const makeInitialNodes = ( data: { applications, plugins, - label: i18next.t('firefly'), - subtitle: nodeName, + nodeName, }, position, }); // Plugins (Right side) Object.entries(plugins).map(([k, v]) => { - nodes = nodes.concat( - v.map((plugin, idx) => { - return { - id: `${PLUGIN_PREFIX}${k}_${idx}`, - targetPosition: Position.Left, - type: 'output', - data: { label: plugin.connection }, - position, - style: nodeStyle, - }; - }) - ); + if (k !== 'events') { + nodes = nodes.concat( + v.map((plugin, idx) => { + return { + id: `${PLUGIN_PREFIX}${k}_${idx}`, + targetPosition: Position.Left, + type: 'output', + data: { label: plugin.name ? plugin.name : plugin.pluginType }, + position, + style: nodeStyle, + }; + }) + ); + } }); return nodes; }; const makeInitialEdges = ( - applications: IWebsocketConnection[], - plugins: IStatus['plugins'] + applications: IStatus['plugins'], + plugins: IStatus['plugins'], + isSmall: boolean ) => { let edges: Edge[] = []; // Apps - edges = edges.concat( - applications.map((_, idx): Edge => { - return { - id: `${APP_PREFIX}${FF_NODE_PREFIX}${idx}`, - source: `${APP_PREFIX}${idx}`, - targetHandle: `${HANDLE_PREFIX}${APP_PREFIX}${idx}`, - target: FF_NODE_PREFIX, - style: edgeStyle, - markerEnd: { - type: MarkerType.ArrowClosed, - color: FFColors.Orange, - }, - label: i18next.t('websocket'), - labelBgPadding: [8, 4], - labelBgBorderRadius: 4, - labelBgStyle: { - fill: '#1e242a', - fillOpacity: 0.97, - }, - labelStyle: { fill: '#FFFFFF', fontWeight: 700 }, - }; - }) - ); + // Applications + Object.entries(applications).map(([k, v], pIdx) => { + // Only show events on left side + if (k === 'events') { + edges = edges.concat( + v.map((_, idx) => { + let edge: Edge = { + id: `${APP_PREFIX}${FF_NODE_PREFIX}${k}_${idx}`, + source: `${APP_PREFIX}${k}_${idx}`, + targetHandle: `${HANDLE_PREFIX}${APP_PREFIX}${pIdx}`, + target: FF_NODE_PREFIX, + style: edgeStyle, + }; + + if (!isSmall) { + edge = { + ...edge, + label: i18next.t(k), + labelBgPadding: [8, 4], + labelBgBorderRadius: 4, + labelBgStyle: { + fill: '#1e242a', + fillOpacity: 0.97, + }, + labelStyle: { fill: '#FFFFFF', fontWeight: 700 }, + }; + } + + return edge; + }) + ); + } + }); + // Plugins Object.entries(plugins).map(([k, v], pIdx) => { - edges = edges.concat( - v.map((_, idx) => { - return { - id: `${PLUGIN_PREFIX}${FF_NODE_PREFIX}${k}_${idx}`, - source: FF_NODE_PREFIX, - sourceHandle: `${HANDLE_PREFIX}${PLUGIN_PREFIX}${pIdx}`, - target: `${PLUGIN_PREFIX}${k}_${idx}`, - style: edgeStyle, - markerEnd: { - type: MarkerType.ArrowClosed, - color: FFColors.Orange, - }, - label: i18next.t(k), - labelBgPadding: [8, 4], - labelBgBorderRadius: 4, - labelBgStyle: { - fill: '#1e242a', - fillOpacity: 0.97, - }, - labelStyle: { fill: '#FFFFFF', fontWeight: 700 }, - }; - }) - ); + if (k !== 'events') { + edges = edges.concat( + v.map((_, idx) => { + let edge: Edge = { + id: `${PLUGIN_PREFIX}${FF_NODE_PREFIX}${k}_${idx}`, + source: FF_NODE_PREFIX, + sourceHandle: `${HANDLE_PREFIX}${PLUGIN_PREFIX}${pIdx}`, + target: `${PLUGIN_PREFIX}${k}_${idx}`, + style: edgeStyle, + }; + + if (!isSmall) { + edge = { + ...edge, + label: i18next.t(k), + labelBgPadding: [8, 4], + labelBgBorderRadius: 4, + labelBgStyle: { + fill: '#1e242a', + fillOpacity: 0.97, + }, + labelStyle: { fill: '#FFFFFF', fontWeight: 700 }, + }; + } + + return edge; + }) + ); + } }); return edges; @@ -203,11 +229,14 @@ const nodeTypes: NodeTypes = { }; interface Props { - applications: IWebsocketConnection[]; plugins: IStatus['plugins']; + isSmall?: boolean; } -export const MyNodeDiagram: React.FC = ({ applications, plugins }) => { +export const MyNodeDiagram: React.FC = ({ + plugins, + isSmall = false, +}) => { const { nodeName } = useContext(ApplicationContext); const [isMounted, setIsMounted] = useState(false); @@ -219,19 +248,15 @@ export const MyNodeDiagram: React.FC = ({ applications, plugins }) => { }, []); const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements( - makeInitialNodes(applications, plugins, nodeName), - makeInitialEdges(applications, plugins) + makeInitialNodes(plugins, plugins, nodeName), + makeInitialEdges(plugins, plugins, isSmall), + isSmall ); const [nodes, , onNodesChange] = useNodesState(layoutedNodes); const [edges, , onEdgesChange] = useEdgesState(layoutedEdges); - if ( - !plugins || - !applications || - applications.length === 0 || - Object.keys(plugins ?? {}).length === 0 - ) { + if (!plugins || Object.keys(plugins ?? {}).length === 0) { return ( JSX.Element = () => { const [recentEvents, setRecentEvents] = useState(); const [isHistLoading, setIsHistLoading] = useState(false); - const [apps, setApps] = useState(); const [plugins, setPlugins] = useState(); const [isMyNodeLoading, setIsMyNodeLoading] = useState(true); @@ -299,12 +296,10 @@ export const HomeDashboard: () => JSX.Element = () => { headerText: t('myNode'), component: !isMyNodeLoading && - apps && - apps.length > 0 && plugins && Object.keys(plugins ?? {}).length > 0 && isMounted ? ( - + ) : ( ), @@ -340,13 +335,6 @@ export const HomeDashboard: () => JSX.Element = () => { useEffect(() => { setIsMyNodeLoading(true); if (isMounted) { - fetchCatcher(`${FF_Paths.apiPrefix}/${FF_Paths.statusWebsockets}`) - .then((wsRes: IWebsocketStatus) => { - isMounted && setApps(wsRes.connections); - }) - .catch((err) => { - reportFetchError(err); - }); fetchCatcher(`${FF_Paths.apiPrefix}/${FF_Paths.status}`) .then((statusRes: IStatus) => { isMounted && setPlugins(statusRes.plugins); diff --git a/src/pages/MyNode/views/Dashboard.tsx b/src/pages/MyNode/views/Dashboard.tsx index 03f845f1..2649cb13 100644 --- a/src/pages/MyNode/views/Dashboard.tsx +++ b/src/pages/MyNode/views/Dashboard.tsx @@ -20,19 +20,13 @@ import { useTranslation } from 'react-i18next'; import { MyNodeDiagram } from '../../../components/Charts/MyNodeDiagram'; import { Header } from '../../../components/Header'; import { SnackbarContext } from '../../../contexts/SnackbarContext'; -import { - FF_Paths, - IStatus, - IWebsocketConnection, - IWebsocketStatus, -} from '../../../interfaces'; +import { FF_Paths, IStatus } from '../../../interfaces'; import { DEFAULT_PADDING } from '../../../theme'; import { fetchCatcher } from '../../../utils'; export const MyNodeDashboard: () => JSX.Element = () => { const { reportFetchError } = useContext(SnackbarContext); const { t } = useTranslation(); - const [apps, setApps] = useState(); const [plugins, setPlugins] = useState(); const [isLoading, setIsLoading] = useState(true); @@ -48,13 +42,6 @@ export const MyNodeDashboard: () => JSX.Element = () => { useEffect(() => { setIsLoading(true); if (isMounted) { - fetchCatcher(`${FF_Paths.apiPrefix}/${FF_Paths.statusWebsockets}`) - .then((wsRes: IWebsocketStatus) => { - isMounted && setApps(wsRes.connections); - }) - .catch((err) => { - reportFetchError(err); - }); fetchCatcher(`${FF_Paths.apiPrefix}/${FF_Paths.status}`) .then((statusRes: IStatus) => { isMounted && setPlugins(statusRes.plugins); @@ -77,13 +64,9 @@ export const MyNodeDashboard: () => JSX.Element = () => { {!isLoading && - apps && - apps.length > 0 && plugins && Object.keys(plugins ?? {}).length > 0 && - isMounted && ( - - )} + isMounted && } diff --git a/src/translations/en.json b/src/translations/en.json index caa35f98..7347fd59 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -57,6 +57,7 @@ "contractListener": "Contract Listener", "contractListeners": "Contract Listeners", "copyToClipboard": "Copy to Clipboard", + "core": "Core", "created": "Created", "customRange": "Custom Range", "dashboard": "Dashboard",