import React, { useContext, useEffect, useRef } from 'react'; import { runMethod } from '../socket'; import { InputGroup, Form, Button } from 'react-bootstrap'; import { DocStringComponent } from './DocStringComponent'; import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { LevelName } from './NotificationsComponent'; import { WebSettingsContext } from '../WebSettings'; interface AsyncMethodProps { name: string; parentPath: string; parameters: Record<string, string>; value: Record<string, string>; docString?: string; hideOutput?: boolean; addNotification: (message: string, levelname?: LevelName) => void; } export const AsyncMethodComponent = React.memo((props: AsyncMethodProps) => { const { name, parentPath, docString, value: runningTask, addNotification } = props; const renderCount = useRef(0); const formRef = useRef(null); const fullAccessPath = [parentPath, name].filter((element) => element).join('.'); const id = getIdFromFullAccessPath(fullAccessPath); const webSettings = useContext(WebSettingsContext); let displayName = name; if (webSettings[fullAccessPath] && webSettings[fullAccessPath].displayName) { displayName = webSettings[fullAccessPath].displayName; } useEffect(() => { renderCount.current++; // updates the value of each form control that has a matching name in the // runningTask object if (runningTask) { const formElement = formRef.current; if (formElement) { Object.entries(runningTask).forEach(([name, value]) => { const inputElement = formElement.elements.namedItem(name); if (inputElement) { inputElement.value = value; } }); } } }, [runningTask]); useEffect(() => { let message: string; if (runningTask === null) { message = `${parentPath}.${name} task was stopped.`; } else { const runningTaskEntries = Object.entries(runningTask) .map(([key, value]) => `${key}: "${value}"`) .join(', '); message = `${parentPath}.${name} was started with parameters { ${runningTaskEntries} }.`; } addNotification(message); }, [props.value]); const execute = async (event: React.FormEvent) => { event.preventDefault(); let method_name: string; const kwargs: Record<string, unknown> = {}; if (runningTask !== undefined && runningTask !== null) { method_name = `stop_${name}`; } else { Object.keys(props.parameters).forEach( (name) => (kwargs[name] = event.target[name].value) ); method_name = `start_${name}`; } runMethod(method_name, parentPath, kwargs); }; const args = Object.entries(props.parameters).map(([name, type], index) => { const form_name = `${name} (${type})`; const value = runningTask && runningTask[name]; const isRunning = value !== undefined && value !== null; return ( <InputGroup key={index}> <InputGroup.Text className="component-label">{form_name}</InputGroup.Text> <Form.Control type="text" name={name} defaultValue={isRunning ? value : ''} disabled={isRunning} /> </InputGroup> ); }); return ( <div className="align-items-center asyncMethodComponent" id={id}> {process.env.NODE_ENV === 'development' && ( <div>Render count: {renderCount.current}</div> )} <h5> Function: {displayName} <DocStringComponent docString={docString} /> </h5> <Form onSubmit={execute} ref={formRef}> {args} <Button id={`button-${id}`} name={name} value={parentPath} type="submit"> {runningTask ? 'Stop' : 'Start'} </Button> </Form> </div> ); });