From fd6aee10f02f4d21f39a8bde6c50819d4f7ef713 Mon Sep 17 00:00:00 2001 From: Theophilus Date: Tue, 11 Jun 2024 16:42:37 +0100 Subject: [PATCH] feat: add starting and stopping states to ActivityDesignerCard --- src/components/designer/custom/NodeInner.tsx | 2 +- .../default/cards/ActivityDesignerCard.tsx | 150 +++++++++--------- src/i18n/locales/en-US.json | 3 + 3 files changed, 82 insertions(+), 73 deletions(-) diff --git a/src/components/designer/custom/NodeInner.tsx b/src/components/designer/custom/NodeInner.tsx index 01be90b19..0a6e051bd 100644 --- a/src/components/designer/custom/NodeInner.tsx +++ b/src/components/designer/custom/NodeInner.tsx @@ -2,11 +2,11 @@ import React from 'react'; import styled from '@emotion/styled'; import { INodeInnerDefaultProps, ISize } from '@mrblenny/react-flow-chart'; import { useTheme } from 'hooks/useTheme'; +import { useStoreState } from 'store'; import { ThemeColors } from 'theme/colors'; import { LOADING_NODE_ID } from 'utils/constants'; import { Loader, StatusBadge } from 'components/common'; import NodeContextMenu from '../NodeContextMenu'; -import { useStoreState } from 'store'; const Styled = { Node: styled.div<{ size?: ISize; colors: ThemeColors['node']; isSelected: boolean }>` diff --git a/src/components/designer/default/cards/ActivityDesignerCard.tsx b/src/components/designer/default/cards/ActivityDesignerCard.tsx index d803ece13..3aee85d2a 100644 --- a/src/components/designer/default/cards/ActivityDesignerCard.tsx +++ b/src/components/designer/default/cards/ActivityDesignerCard.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; +import { useAsyncCallback } from 'react-async-hook'; import { ArrowDownOutlined, ArrowRightOutlined, @@ -11,7 +12,6 @@ import { Alert, Button, Tooltip } from 'antd'; import { usePrefixedTranslation } from 'hooks'; import { useTheme } from 'hooks/useTheme'; import { Status } from 'shared/types'; -import { getDocker } from 'lib/docker/dockerService'; import { useStoreActions } from 'store'; import { ThemeColors } from 'theme/colors'; import { ActivityInfo, Network, SimulationActivity } from 'types'; @@ -152,7 +152,7 @@ const defaultActivityInfo: ActivityInfo = { }; const ActivityDesignerCard: React.FC = ({ visible, network }) => { - const [isSimulationActive, setIsStartSimulationActive] = React.useState(false); + const [isSimulationActive, setIsSimulationActive] = React.useState(false); const [isAddActivityActive, setIsAddActivityActive] = React.useState(false); const [addActivityInvalidState, setAddActivityInvalidState] = useState(null); @@ -168,81 +168,72 @@ const ActivityDesignerCard: React.FC = ({ visible, network }) => { startSimulation, stopSimulation, } = useStoreActions(s => s.network); + const { notify } = useStoreActions(s => s.app); const { lightning } = network.nodes; const activities = network.simulationActivities ?? []; const numberOfActivities = activities.length; - const isSimulationContainerRunning = async () => { - const docker = await getDocker(); - const containers = await docker.listContainers(); - const simContainer = containers.find(c => { - // remove the leading '/' from the container name - const name = c.Names[0].substring(1); - return name === `polar-n${network.id}-simln`; - }); - return simContainer?.State === 'restarting' || simContainer?.State === 'running'; - }; - - useEffect(() => { - isSimulationContainerRunning().then(isRunning => { - console.log('isRunning', isRunning); - setIsStartSimulationActive(isRunning); - }); - }, [isSimulationContainerRunning]); - - const startSimulationActivity = () => { - if (network.status !== Status.Started) { - setAddActivityInvalidState({ - state: 'warning', - message: l('startWarning'), - action: 'start', - }); - setIsStartSimulationActive(false); - return; - } - if (numberOfActivities === 0) { - setIsAddActivityActive(true); - setAddActivityInvalidState({ - state: 'warning', - message: l('NoActivityAddedWarning'), - action: 'start', - }); - setIsStartSimulationActive(false); - return; - } - const allNotStartedNodesSet = new Set(); - const nodes = network.simulationActivities.flatMap(activity => { - const activityNodes = new Set([activity.source.label, activity.destination.label]); - return lightning - .filter(node => node.status !== Status.Started && activityNodes.has(node.name)) - .filter(node => { - const notStarted = !allNotStartedNodesSet.has(node.name); - if (notStarted) { - allNotStartedNodesSet.add(node.name); - } - return notStarted; + const startSimulationActivity = useAsyncCallback(async () => { + try { + if (network.status !== Status.Started) { + setAddActivityInvalidState({ + state: 'warning', + message: l('startWarning'), + action: 'start', }); - }); - if (nodes.length > 0) { - setIsAddActivityActive(true); - setAddActivityInvalidState({ - state: 'warning', - message: l('startWarning'), - action: 'start', + setIsSimulationActive(false); + return; + } + if (numberOfActivities === 0) { + setIsAddActivityActive(true); + setAddActivityInvalidState({ + state: 'warning', + message: l('NoActivityAddedWarning'), + action: 'start', + }); + setIsSimulationActive(false); + return; + } + const allNotStartedNodesSet = new Set(); + const nodes = network.simulationActivities.flatMap(activity => { + const activityNodes = new Set([ + activity.source.label, + activity.destination.label, + ]); + return lightning + .filter(node => node.status !== Status.Started && activityNodes.has(node.name)) + .filter(node => { + const notStarted = !allNotStartedNodesSet.has(node.name); + if (notStarted) { + allNotStartedNodesSet.add(node.name); + } + return notStarted; + }); }); - setIsStartSimulationActive(false); - return; - } - setAddActivityInvalidState(null); - if (isSimulationActive) { - setIsStartSimulationActive(false); - stopSimulation({ id: network.id }); - return; + if (nodes.length > 0) { + setIsAddActivityActive(true); + setAddActivityInvalidState({ + state: 'warning', + message: l('startWarning'), + action: 'start', + }); + setIsSimulationActive(false); + return; + } + setAddActivityInvalidState(null); + if (isSimulationActive) { + setIsSimulationActive(false); + await stopSimulation({ id: network.id }); + return; + } + setIsSimulationActive(true); + await startSimulation({ id: network.id }); + } catch (error: any) { + setIsSimulationActive(false); + notify({ message: l('startError'), error }); } - setIsStartSimulationActive(true); - startSimulation({ id: network.id }); - }; + }); const toggleAddActivity = () => { setIsAddActivityActive(prev => !prev); @@ -295,6 +286,19 @@ const ActivityDesignerCard: React.FC = ({ visible, network }) => { setActivityInfo(defaultActivityInfo); }; + const primaryCtaText = () => { + if (!isSimulationActive && startSimulationActivity.loading) { + return l('stopping'); + } + if (isSimulationActive && startSimulationActivity.loading) { + return l('starting'); + } + if (isSimulationActive && !startSimulationActivity.loading) { + return l('stop'); + } + return l('start'); + }; + if (!visible) return null; return ( <> @@ -367,9 +371,11 @@ const ActivityDesignerCard: React.FC = ({ visible, network }) => { - {isSimulationActive ? l('stop') : l('start')} + {primaryCtaText()} ); diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 09556ea71..19c16a1f7 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -98,12 +98,15 @@ "cmps.designer.default.cards.NetworkDesignerCard.addNodesTitle": "Add Nodes", "cmps.designer.default.cards.NetworkDesignerCard.addNodesDesc": "Drag a node below onto the canvas to add it to the network", "cmps.designer.default.cards.ActivityDesignerCard.mainDesc": "Click on an element in the designer to see details", + "cmps.designer.default.cards.ActivityDesignerCard.startError": "Unable to start the simulation for the network", "cmps.designer.default.cards.ActivityDesignerCard.addActivitiesTitle": "Add Activities", "cmps.designer.default.cards.ActivityDesignerCard.addActivitiesDesc": "Run a simulation between running nodes in the network", "cmps.designer.default.cards.ActivityDesignerCard.activities": "Activities", "cmps.designer.default.cards.ActivityDesignerCard.duplicateBtnTip": "Duplicate", "cmps.designer.default.cards.ActivityDesignerCard.start": "Start", "cmps.designer.default.cards.ActivityDesignerCard.stop": "Stop", + "cmps.designer.default.cards.ActivityDesignerCard.stopping": "Stopping", + "cmps.designer.default.cards.ActivityDesignerCard.starting": "Starting", "cmps.designer.default.cards.ActivityDesignerCard.startWarning": "The network and activity nodes must be started to run simulations", "cmps.designer.default.cards.ActivityDesignerCard.NoActivityAddedWarning": "Please add at least one activity to run simulations", "cmps.designer.default.cards.NetworkDesignerCard.showVersions": "Show All Versions",