fix(ui): memoize everything nodes

This commit is contained in:
psychedelicious 2023-04-22 22:03:27 +10:00
parent 4901911c1a
commit 43addc1548
22 changed files with 81 additions and 57 deletions

View File

@ -1,7 +1,7 @@
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import 'reactflow/dist/style.css'; import 'reactflow/dist/style.css';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { import {
Tooltip, Tooltip,
Menu, Menu,
@ -21,7 +21,7 @@ import { makeToast } from 'features/system/hooks/useToastWatcher';
import { IAIIconButton } from 'exports'; import { IAIIconButton } from 'exports';
import { AnyInvocationType } from 'services/events/types'; import { AnyInvocationType } from 'services/events/types';
export const AddNodeMenu = () => { const AddNodeMenu = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const invocationTemplates = useAppSelector( const invocationTemplates = useAppSelector(
@ -67,3 +67,5 @@ export const AddNodeMenu = () => {
</Menu> </Menu>
); );
}; };
export default memo(AddNodeMenu);

View File

@ -2,8 +2,9 @@ import 'reactflow/dist/style.css';
import { Tooltip, Badge, HStack } from '@chakra-ui/react'; import { Tooltip, Badge, HStack } from '@chakra-ui/react';
import { map } from 'lodash'; import { map } from 'lodash';
import { FIELDS } from '../types/constants'; import { FIELDS } from '../types/constants';
import { memo } from 'react';
export const FieldTypeLegend = () => { const FieldTypeLegend = () => {
return ( return (
<HStack> <HStack>
{map(FIELDS, ({ title, description, color }, key) => ( {map(FIELDS, ({ title, description, color }, key) => (
@ -16,3 +17,5 @@ export const FieldTypeLegend = () => {
</HStack> </HStack>
); );
}; };
export default memo(FieldTypeLegend);

View File

@ -1,15 +1,12 @@
import { import {
Background, Background,
Controls,
MiniMap, MiniMap,
OnConnect, OnConnect,
OnEdgesChange, OnEdgesChange,
OnNodesChange, OnNodesChange,
ReactFlow, ReactFlow,
ConnectionLineType,
OnConnectStart, OnConnectStart,
OnConnectEnd, OnConnectEnd,
Panel,
} from 'reactflow'; } from 'reactflow';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
@ -20,16 +17,8 @@ import {
edgesChanged, edgesChanged,
nodesChanged, nodesChanged,
} from '../store/nodesSlice'; } from '../store/nodesSlice';
import { useCallback, useState } from 'react'; import { useCallback } from 'react';
import { InvocationComponent } from './InvocationComponent'; import { InvocationComponent } from './InvocationComponent';
import { AddNodeMenu } from './AddNodeMenu';
import { FieldTypeLegend } from './FieldTypeLegend';
import { Button } from '@chakra-ui/react';
import { nodesGraphBuilt } from 'services/thunks/session';
import { IAIIconButton } from 'exports';
import { InfoIcon } from '@chakra-ui/icons';
import { ViewportControls } from './ViewportControls';
import NodeGraphOverlay from './NodeGraphOverlay';
import TopLeftPanel from './panels/TopLeftPanel'; import TopLeftPanel from './panels/TopLeftPanel';
import TopRightPanel from './panels/TopRightPanel'; import TopRightPanel from './panels/TopRightPanel';
import TopCenterPanel from './panels/TopCenterPanel'; import TopCenterPanel from './panels/TopCenterPanel';

View File

@ -1,6 +1,6 @@
import { Flex, Heading, Tooltip, Icon } from '@chakra-ui/react'; import { Flex, Heading, Tooltip, Icon } from '@chakra-ui/react';
import { InvocationTemplate } from 'features/nodes/types/types'; import { InvocationTemplate } from 'features/nodes/types/types';
import { MutableRefObject } from 'react'; import { memo, MutableRefObject } from 'react';
import { FaInfoCircle } from 'react-icons/fa'; import { FaInfoCircle } from 'react-icons/fa';
interface IAINodeHeaderProps { interface IAINodeHeaderProps {
@ -8,7 +8,7 @@ interface IAINodeHeaderProps {
template: InvocationTemplate; template: InvocationTemplate;
} }
export default function IAINodeHeader(props: IAINodeHeaderProps) { const IAINodeHeader = (props: IAINodeHeaderProps) => {
const { nodeId, template } = props; const { nodeId, template } = props;
return ( return (
<Flex <Flex
@ -34,4 +34,6 @@ export default function IAINodeHeader(props: IAINodeHeaderProps) {
</Tooltip> </Tooltip>
</Flex> </Flex>
); );
} };
export default memo(IAINodeHeader);

View File

@ -19,7 +19,7 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import FieldHandle from '../FieldHandle'; import FieldHandle from '../FieldHandle';
import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection'; import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection';
import { InputFieldComponent } from '../InputFieldComponent'; import InputFieldComponent from '../InputFieldComponent';
import { FaInfoCircle } from 'react-icons/fa'; import { FaInfoCircle } from 'react-icons/fa';
import { HANDLE_TOOLTIP_OPEN_DELAY } from 'features/nodes/types/constants'; import { HANDLE_TOOLTIP_OPEN_DELAY } from 'features/nodes/types/constants';

View File

@ -1,6 +1,7 @@
import { memo } from 'react';
import { NodeResizeControl, NodeResizerProps } from 'reactflow'; import { NodeResizeControl, NodeResizerProps } from 'reactflow';
export default function IAINodeResizer(props: NodeResizerProps) { const IAINodeResizer = (props: NodeResizerProps) => {
const { ...rest } = props; const { ...rest } = props;
return ( return (
<NodeResizeControl <NodeResizeControl
@ -17,4 +18,6 @@ export default function IAINodeResizer(props: NodeResizerProps) {
{...rest} {...rest}
></NodeResizeControl> ></NodeResizeControl>
); );
} };
export default memo(IAINodeResizer);

View File

@ -1,13 +1,14 @@
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { memo } from 'react';
import { InputFieldTemplate, InputFieldValue } from '../types/types'; import { InputFieldTemplate, InputFieldValue } from '../types/types';
import { ArrayInputFieldComponent } from './fields/ArrayInputFieldComponent'; import ArrayInputFieldComponent from './fields/ArrayInputFieldComponent';
import { BooleanInputFieldComponent } from './fields/BooleanInputFieldComponent'; import BooleanInputFieldComponent from './fields/BooleanInputFieldComponent';
import { EnumInputFieldComponent } from './fields/EnumInputFieldComponent'; import EnumInputFieldComponent from './fields/EnumInputFieldComponent';
import { ImageInputFieldComponent } from './fields/ImageInputFieldComponent'; import ImageInputFieldComponent from './fields/ImageInputFieldComponent';
import { LatentsInputFieldComponent } from './fields/LatentsInputFieldComponent'; import LatentsInputFieldComponent from './fields/LatentsInputFieldComponent';
import { ModelInputFieldComponent } from './fields/ModelInputFieldComponent'; import ModelInputFieldComponent from './fields/ModelInputFieldComponent';
import { NumberInputFieldComponent } from './fields/NumberInputFieldComponent'; import NumberInputFieldComponent from './fields/NumberInputFieldComponent';
import { StringInputFieldComponent } from './fields/StringInputFieldComponent'; import StringInputFieldComponent from './fields/StringInputFieldComponent';
type InputFieldComponentProps = { type InputFieldComponentProps = {
nodeId: string; nodeId: string;
@ -16,7 +17,7 @@ type InputFieldComponentProps = {
}; };
// build an individual input element based on the schema // build an individual input element based on the schema
export const InputFieldComponent = (props: InputFieldComponentProps) => { const InputFieldComponent = (props: InputFieldComponentProps) => {
const { nodeId, field, template } = props; const { nodeId, field, template } = props;
const { type, value } = field; const { type, value } = field;
@ -105,3 +106,5 @@ export const InputFieldComponent = (props: InputFieldComponentProps) => {
return <Box p={2}>Unknown field type: {type}</Box>; return <Box p={2}>Unknown field type: {type}</Box>;
}; };
export default memo(InputFieldComponent);

View File

@ -1,14 +1,12 @@
import { NodeProps, NodeResizeControl } from 'reactflow'; import { NodeProps } from 'reactflow';
import { Box, Flex, Icon, useToken } from '@chakra-ui/react'; import { Box, Flex, Icon, useToken } from '@chakra-ui/react';
import { FaExclamationCircle } from 'react-icons/fa'; import { FaExclamationCircle } from 'react-icons/fa';
import { InvocationTemplate, InvocationValue } from '../types/types'; import { InvocationTemplate, InvocationValue } from '../types/types';
import { memo, PropsWithChildren, useMemo, useRef } from 'react'; import { memo, PropsWithChildren, useMemo } from 'react';
import { useGetInvocationTemplate } from '../hooks/useInvocationTemplate';
import IAINodeOutputs from './IAINode/IAINodeOutputs'; import IAINodeOutputs from './IAINode/IAINodeOutputs';
import IAINodeInputs from './IAINode/IAINodeInputs'; import IAINodeInputs from './IAINode/IAINodeInputs';
import IAINodeHeader from './IAINode/IAINodeHeader'; import IAINodeHeader from './IAINode/IAINodeHeader';
import { IoResize } from 'react-icons/io5';
import IAINodeResizer from './IAINode/IAINodeResizer'; import IAINodeResizer from './IAINode/IAINodeResizer';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { AnyInvocationType } from 'services/events/types'; import { AnyInvocationType } from 'services/events/types';

View File

@ -3,6 +3,7 @@ import { Box } from '@chakra-ui/react';
import { ReactFlowProvider } from 'reactflow'; import { ReactFlowProvider } from 'reactflow';
import { Flow } from './Flow'; import { Flow } from './Flow';
import { memo } from 'react';
const NodeEditor = () => { const NodeEditor = () => {
return ( return (
@ -22,4 +23,4 @@ const NodeEditor = () => {
); );
}; };
export default NodeEditor; export default memo(NodeEditor);

View File

@ -1,9 +1,10 @@
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import { RootState } from 'app/store'; import { RootState } from 'app/store';
import { useAppSelector } from 'app/storeHooks'; import { useAppSelector } from 'app/storeHooks';
import { memo } from 'react';
import { buildNodesGraph } from '../util/nodesGraphBuilder/buildNodesGraph'; import { buildNodesGraph } from '../util/nodesGraphBuilder/buildNodesGraph';
export default function NodeGraphOverlay() { const NodeGraphOverlay = () => {
const state = useAppSelector((state: RootState) => state); const state = useAppSelector((state: RootState) => state);
const graph = buildNodesGraph(state); const graph = buildNodesGraph(state);
@ -14,7 +15,6 @@ export default function NodeGraphOverlay() {
position="absolute" position="absolute"
top={10} top={10}
right={2} right={2}
userSelect="none"
opacity={0.7} opacity={0.7}
background="base.800" background="base.800"
p={2} p={2}
@ -25,4 +25,6 @@ export default function NodeGraphOverlay() {
{JSON.stringify(graph, null, 2)} {JSON.stringify(graph, null, 2)}
</Box> </Box>
); );
} };
export default memo(NodeGraphOverlay);

View File

@ -1,12 +1,12 @@
import { ButtonGroup } from '@chakra-ui/react'; import { ButtonGroup } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { IAIIconButton } from 'exports'; import { IAIIconButton } from 'exports';
import { useCallback } from 'react'; import { memo, useCallback } from 'react';
import { FaCode, FaExpand, FaMinus, FaPlus } from 'react-icons/fa'; import { FaCode, FaExpand, FaMinus, FaPlus } from 'react-icons/fa';
import { useReactFlow } from 'reactflow'; import { useReactFlow } from 'reactflow';
import { shouldShowGraphOverlayChanged } from '../store/nodesSlice'; import { shouldShowGraphOverlayChanged } from '../store/nodesSlice';
export const ViewportControls = () => { const ViewportControls = () => {
const { zoomIn, zoomOut, fitView } = useReactFlow(); const { zoomIn, zoomOut, fitView } = useReactFlow();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const shouldShowGraphOverlay = useAppSelector( const shouldShowGraphOverlay = useAppSelector(
@ -55,3 +55,5 @@ export const ViewportControls = () => {
</ButtonGroup> </ButtonGroup>
); );
}; };
export default memo(ViewportControls);

View File

@ -2,13 +2,16 @@ import {
ArrayInputFieldTemplate, ArrayInputFieldTemplate,
ArrayInputFieldValue, ArrayInputFieldValue,
} from 'features/nodes/types/types'; } from 'features/nodes/types/types';
import { FaImage, FaList } from 'react-icons/fa'; import { memo } from 'react';
import { FaList } from 'react-icons/fa';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
export const ArrayInputFieldComponent = ( const ArrayInputFieldComponent = (
props: FieldComponentProps<ArrayInputFieldValue, ArrayInputFieldTemplate> props: FieldComponentProps<ArrayInputFieldValue, ArrayInputFieldTemplate>
) => { ) => {
const { nodeId, field } = props; const { nodeId, field } = props;
return <FaList />; return <FaList />;
}; };
export default memo(ArrayInputFieldComponent);

View File

@ -5,10 +5,10 @@ import {
BooleanInputFieldTemplate, BooleanInputFieldTemplate,
BooleanInputFieldValue, BooleanInputFieldValue,
} from 'features/nodes/types/types'; } from 'features/nodes/types/types';
import { ChangeEvent } from 'react'; import { ChangeEvent, memo } from 'react';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
export const BooleanInputFieldComponent = ( const BooleanInputFieldComponent = (
props: FieldComponentProps<BooleanInputFieldValue, BooleanInputFieldTemplate> props: FieldComponentProps<BooleanInputFieldValue, BooleanInputFieldTemplate>
) => { ) => {
const { nodeId, field } = props; const { nodeId, field } = props;
@ -29,3 +29,5 @@ export const BooleanInputFieldComponent = (
<Switch onChange={handleValueChanged} isChecked={field.value}></Switch> <Switch onChange={handleValueChanged} isChecked={field.value}></Switch>
); );
}; };
export default memo(BooleanInputFieldComponent);

View File

@ -5,10 +5,10 @@ import {
EnumInputFieldTemplate, EnumInputFieldTemplate,
EnumInputFieldValue, EnumInputFieldValue,
} from 'features/nodes/types/types'; } from 'features/nodes/types/types';
import { ChangeEvent } from 'react'; import { ChangeEvent, memo } from 'react';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
export const EnumInputFieldComponent = ( const EnumInputFieldComponent = (
props: FieldComponentProps<EnumInputFieldValue, EnumInputFieldTemplate> props: FieldComponentProps<EnumInputFieldValue, EnumInputFieldTemplate>
) => { ) => {
const { nodeId, field, template } = props; const { nodeId, field, template } = props;
@ -33,3 +33,5 @@ export const EnumInputFieldComponent = (
</Select> </Select>
); );
}; };
export default memo(EnumInputFieldComponent);

View File

@ -9,12 +9,12 @@ import {
ImageInputFieldTemplate, ImageInputFieldTemplate,
ImageInputFieldValue, ImageInputFieldValue,
} from 'features/nodes/types/types'; } from 'features/nodes/types/types';
import { DragEvent, useCallback, useState } from 'react'; import { DragEvent, memo, useCallback, useState } from 'react';
import { FaImage } from 'react-icons/fa'; import { FaImage } from 'react-icons/fa';
import { ImageType } from 'services/api'; import { ImageType } from 'services/api';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
export const ImageInputFieldComponent = ( const ImageInputFieldComponent = (
props: FieldComponentProps<ImageInputFieldValue, ImageInputFieldTemplate> props: FieldComponentProps<ImageInputFieldValue, ImageInputFieldTemplate>
) => { ) => {
const { nodeId, field } = props; const { nodeId, field } = props;
@ -62,3 +62,5 @@ export const ImageInputFieldComponent = (
</Box> </Box>
); );
}; };
export default memo(ImageInputFieldComponent);

View File

@ -2,12 +2,15 @@ import {
LatentsInputFieldTemplate, LatentsInputFieldTemplate,
LatentsInputFieldValue, LatentsInputFieldValue,
} from 'features/nodes/types/types'; } from 'features/nodes/types/types';
import { memo } from 'react';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
export const LatentsInputFieldComponent = ( const LatentsInputFieldComponent = (
props: FieldComponentProps<LatentsInputFieldValue, LatentsInputFieldTemplate> props: FieldComponentProps<LatentsInputFieldValue, LatentsInputFieldTemplate>
) => { ) => {
const { nodeId, field } = props; const { nodeId, field } = props;
return null; return null;
}; };
export default memo(LatentsInputFieldComponent);

View File

@ -12,7 +12,7 @@ import {
selectModelsIds, selectModelsIds,
} from 'features/system/store/modelSlice'; } from 'features/system/store/modelSlice';
import { isEqual, map } from 'lodash'; import { isEqual, map } from 'lodash';
import { ChangeEvent } from 'react'; import { ChangeEvent, memo } from 'react';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
const availableModelsSelector = createSelector( const availableModelsSelector = createSelector(
@ -28,7 +28,7 @@ const availableModelsSelector = createSelector(
} }
); );
export const ModelInputFieldComponent = ( const ModelInputFieldComponent = (
props: FieldComponentProps<ModelInputFieldValue, ModelInputFieldTemplate> props: FieldComponentProps<ModelInputFieldValue, ModelInputFieldTemplate>
) => { ) => {
const { nodeId, field } = props; const { nodeId, field } = props;
@ -55,3 +55,5 @@ export const ModelInputFieldComponent = (
</Select> </Select>
); );
}; };
export default memo(ModelInputFieldComponent);

View File

@ -13,9 +13,10 @@ import {
IntegerInputFieldTemplate, IntegerInputFieldTemplate,
IntegerInputFieldValue, IntegerInputFieldValue,
} from 'features/nodes/types/types'; } from 'features/nodes/types/types';
import { memo } from 'react';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
export const NumberInputFieldComponent = ( const NumberInputFieldComponent = (
props: FieldComponentProps< props: FieldComponentProps<
IntegerInputFieldValue | FloatInputFieldValue, IntegerInputFieldValue | FloatInputFieldValue,
IntegerInputFieldTemplate | FloatInputFieldTemplate IntegerInputFieldTemplate | FloatInputFieldTemplate
@ -39,3 +40,5 @@ export const NumberInputFieldComponent = (
</NumberInput> </NumberInput>
); );
}; };
export default memo(NumberInputFieldComponent);

View File

@ -5,10 +5,10 @@ import {
StringInputFieldTemplate, StringInputFieldTemplate,
StringInputFieldValue, StringInputFieldValue,
} from 'features/nodes/types/types'; } from 'features/nodes/types/types';
import { ChangeEvent } from 'react'; import { ChangeEvent, memo } from 'react';
import { FieldComponentProps } from './types'; import { FieldComponentProps } from './types';
export const StringInputFieldComponent = ( const StringInputFieldComponent = (
props: FieldComponentProps<StringInputFieldValue, StringInputFieldTemplate> props: FieldComponentProps<StringInputFieldValue, StringInputFieldTemplate>
) => { ) => {
const { nodeId, field } = props; const { nodeId, field } = props;
@ -27,3 +27,5 @@ export const StringInputFieldComponent = (
return <Input onChange={handleValueChanged} value={field.value}></Input>; return <Input onChange={handleValueChanged} value={field.value}></Input>;
}; };
export default memo(StringInputFieldComponent);

View File

@ -1,6 +1,6 @@
import { memo } from 'react'; import { memo } from 'react';
import { Panel } from 'reactflow'; import { Panel } from 'reactflow';
import { ViewportControls } from '../ViewportControls'; import ViewportControls from '../ViewportControls';
const BottomLeftPanel = () => ( const BottomLeftPanel = () => (
<Panel position="bottom-left"> <Panel position="bottom-left">

View File

@ -1,6 +1,6 @@
import { memo } from 'react'; import { memo } from 'react';
import { Panel } from 'reactflow'; import { Panel } from 'reactflow';
import { AddNodeMenu } from '../AddNodeMenu'; import AddNodeMenu from '../AddNodeMenu';
const TopLeftPanel = () => ( const TopLeftPanel = () => (
<Panel position="top-left"> <Panel position="top-left">

View File

@ -2,7 +2,7 @@ import { RootState } from 'app/store';
import { useAppSelector } from 'app/storeHooks'; import { useAppSelector } from 'app/storeHooks';
import { memo } from 'react'; import { memo } from 'react';
import { Panel } from 'reactflow'; import { Panel } from 'reactflow';
import { FieldTypeLegend } from '../FieldTypeLegend'; import FieldTypeLegend from '../FieldTypeLegend';
import NodeGraphOverlay from '../NodeGraphOverlay'; import NodeGraphOverlay from '../NodeGraphOverlay';
const TopRightPanel = () => { const TopRightPanel = () => {