mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): remove extraneous selectedEdges and selectedNodes state
This commit is contained in:
parent
b0c7c7cb47
commit
6cf5b402c6
@ -15,8 +15,6 @@ import {
|
|||||||
nodesDeleted,
|
nodesDeleted,
|
||||||
redo,
|
redo,
|
||||||
selectedAll,
|
selectedAll,
|
||||||
selectedEdgesChanged,
|
|
||||||
selectedNodesChanged,
|
|
||||||
undo,
|
undo,
|
||||||
viewportChanged,
|
viewportChanged,
|
||||||
} from 'features/nodes/store/nodesSlice';
|
} from 'features/nodes/store/nodesSlice';
|
||||||
@ -32,7 +30,6 @@ import type {
|
|||||||
OnMoveEnd,
|
OnMoveEnd,
|
||||||
OnNodesChange,
|
OnNodesChange,
|
||||||
OnNodesDelete,
|
OnNodesDelete,
|
||||||
OnSelectionChangeFunc,
|
|
||||||
ProOptions,
|
ProOptions,
|
||||||
ReactFlowProps,
|
ReactFlowProps,
|
||||||
} from 'reactflow';
|
} from 'reactflow';
|
||||||
@ -111,14 +108,6 @@ export const Flow = memo(() => {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSelectionChange: OnSelectionChangeFunc = useCallback(
|
|
||||||
({ nodes, edges }) => {
|
|
||||||
dispatch(selectedNodesChanged(nodes ? nodes.map((n) => n.id) : []));
|
|
||||||
dispatch(selectedEdgesChanged(edges ? edges.map((e) => e.id) : []));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMoveEnd: OnMoveEnd = useCallback(
|
const handleMoveEnd: OnMoveEnd = useCallback(
|
||||||
(e, viewport) => {
|
(e, viewport) => {
|
||||||
dispatch(viewportChanged(viewport));
|
dispatch(viewportChanged(viewport));
|
||||||
@ -258,7 +247,6 @@ export const Flow = memo(() => {
|
|||||||
onConnectEnd={onConnectEnd}
|
onConnectEnd={onConnectEnd}
|
||||||
onMoveEnd={handleMoveEnd}
|
onMoveEnd={handleMoveEnd}
|
||||||
connectionLineComponent={CustomConnectionLine}
|
connectionLineComponent={CustomConnectionLine}
|
||||||
onSelectionChange={handleSelectionChange}
|
|
||||||
isValidConnection={isValidConnection}
|
isValidConnection={isValidConnection}
|
||||||
minZoom={0.1}
|
minZoom={0.1}
|
||||||
snapToGrid={shouldSnapToGrid}
|
snapToGrid={shouldSnapToGrid}
|
||||||
|
@ -3,27 +3,21 @@ import { useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||||
|
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
const selector = createMemoizedSelector(selectNodesSlice, (nodes) => {
|
|
||||||
const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
|
||||||
|
|
||||||
const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId);
|
const selector = createMemoizedSelector(selectNodesSlice, (nodes) => selectLastSelectedNode(nodes));
|
||||||
|
|
||||||
return {
|
|
||||||
data: lastSelectedNode?.data,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const InspectorDataTab = () => {
|
const InspectorDataTab = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { data } = useAppSelector(selector);
|
const lastSelectedNode = useAppSelector(selector);
|
||||||
|
|
||||||
if (!data) {
|
if (!lastSelectedNode) {
|
||||||
return <IAINoContentFallback label={t('nodes.noNodeSelected')} icon={null} />;
|
return <IAINoContentFallback label={t('nodes.noNodeSelected')} icon={null} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <DataViewer data={data} label="Node Data" />;
|
return <DataViewer data={lastSelectedNode.data} label="Node Data" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(InspectorDataTab);
|
export default memo(InspectorDataTab);
|
||||||
|
@ -7,6 +7,7 @@ import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableCon
|
|||||||
import NotesTextarea from 'features/nodes/components/flow/nodes/Invocation/NotesTextarea';
|
import NotesTextarea from 'features/nodes/components/flow/nodes/Invocation/NotesTextarea';
|
||||||
import { useNodeNeedsUpdate } from 'features/nodes/hooks/useNodeNeedsUpdate';
|
import { useNodeNeedsUpdate } from 'features/nodes/hooks/useNodeNeedsUpdate';
|
||||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||||
|
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -18,8 +19,7 @@ const InspectorDetailsTab = () => {
|
|||||||
const selector = useMemo(
|
const selector = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
||||||
const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
const lastSelectedNode = selectLastSelectedNode(nodes);
|
||||||
const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId);
|
|
||||||
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
||||||
|
|
||||||
if (!isInvocationNode(lastSelectedNode) || !lastSelectedNodeTemplate) {
|
if (!isInvocationNode(lastSelectedNode) || !lastSelectedNodeTemplate) {
|
||||||
|
@ -6,6 +6,7 @@ import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
|||||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||||
|
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -19,11 +20,10 @@ const InspectorOutputsTab = () => {
|
|||||||
const selector = useMemo(
|
const selector = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
||||||
const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
const lastSelectedNode = selectLastSelectedNode(nodes);
|
||||||
const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId);
|
|
||||||
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
||||||
|
|
||||||
const nes = nodes.nodeExecutionStates[lastSelectedNodeId ?? '__UNKNOWN_NODE__'];
|
const nes = nodes.nodeExecutionStates[lastSelectedNode?.id ?? '__UNKNOWN_NODE__'];
|
||||||
|
|
||||||
if (!isInvocationNode(lastSelectedNode) || !nes || !lastSelectedNodeTemplate) {
|
if (!isInvocationNode(lastSelectedNode) || !nes || !lastSelectedNodeTemplate) {
|
||||||
return;
|
return;
|
||||||
|
@ -4,6 +4,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||||
|
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
@ -12,8 +13,7 @@ const NodeTemplateInspector = () => {
|
|||||||
const selector = useMemo(
|
const selector = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
||||||
const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
const lastSelectedNode = selectLastSelectedNode(nodes);
|
||||||
const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId);
|
|
||||||
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
||||||
|
|
||||||
return lastSelectedNodeTemplate;
|
return lastSelectedNodeTemplate;
|
||||||
|
@ -74,8 +74,6 @@ const initialNodesState: NodesState = {
|
|||||||
_version: 1,
|
_version: 1,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
edges: [],
|
edges: [],
|
||||||
selectedNodes: [],
|
|
||||||
selectedEdges: [],
|
|
||||||
nodeExecutionStates: {},
|
nodeExecutionStates: {},
|
||||||
viewport: { x: 0, y: 0, zoom: 1 },
|
viewport: { x: 0, y: 0, zoom: 1 },
|
||||||
};
|
};
|
||||||
@ -351,12 +349,6 @@ export const nodesSlice = createSlice({
|
|||||||
state.nodes
|
state.nodes
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
selectedNodesChanged: (state, action: PayloadAction<string[]>) => {
|
|
||||||
state.selectedNodes = action.payload;
|
|
||||||
},
|
|
||||||
selectedEdgesChanged: (state, action: PayloadAction<string[]>) => {
|
|
||||||
state.selectedEdges = action.payload;
|
|
||||||
},
|
|
||||||
fieldValueReset: (state, action: FieldValueAction<StatefulFieldValue>) => {
|
fieldValueReset: (state, action: FieldValueAction<StatefulFieldValue>) => {
|
||||||
fieldValueReducer(state, action, zStatefulFieldValue);
|
fieldValueReducer(state, action, zStatefulFieldValue);
|
||||||
},
|
},
|
||||||
@ -593,8 +585,6 @@ export const {
|
|||||||
nodeUseCacheChanged,
|
nodeUseCacheChanged,
|
||||||
notesNodeValueChanged,
|
notesNodeValueChanged,
|
||||||
selectedAll,
|
selectedAll,
|
||||||
selectedEdgesChanged,
|
|
||||||
selectedNodesChanged,
|
|
||||||
selectionPasted,
|
selectionPasted,
|
||||||
viewportChanged,
|
viewportChanged,
|
||||||
edgeAdded,
|
edgeAdded,
|
||||||
@ -602,6 +592,78 @@ export const {
|
|||||||
redo,
|
redo,
|
||||||
} = nodesSlice.actions;
|
} = nodesSlice.actions;
|
||||||
|
|
||||||
|
export const $cursorPos = atom<XYPosition | null>(null);
|
||||||
|
export const $templates = atom<Templates>({});
|
||||||
|
export const $copiedNodes = atom<AnyNode[]>([]);
|
||||||
|
export const $copiedEdges = atom<InvocationNodeEdge[]>([]);
|
||||||
|
export const $pendingConnection = atom<PendingConnection | null>(null);
|
||||||
|
export const $isModifyingEdge = atom(false);
|
||||||
|
export const $isAddNodePopoverOpen = atom(false);
|
||||||
|
export const closeAddNodePopover = () => {
|
||||||
|
$isAddNodePopoverOpen.set(false);
|
||||||
|
$pendingConnection.set(null);
|
||||||
|
};
|
||||||
|
export const openAddNodePopover = () => {
|
||||||
|
$isAddNodePopoverOpen.set(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectNodesSlice = (state: RootState) => state.nodes.present;
|
||||||
|
|
||||||
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
|
const migrateNodesState = (state: any): any => {
|
||||||
|
if (!('_version' in state)) {
|
||||||
|
state._version = 1;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const nodesPersistConfig: PersistConfig<NodesState> = {
|
||||||
|
name: nodesSlice.name,
|
||||||
|
initialState: initialNodesState,
|
||||||
|
migrate: migrateNodesState,
|
||||||
|
persistDenylist: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectionMatcher = isAnyOf(selectedAll, selectionPasted, nodeExclusivelySelected);
|
||||||
|
|
||||||
|
const isSelectionAction = (action: UnknownAction) => {
|
||||||
|
if (selectionMatcher(action)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (nodesChanged.match(action)) {
|
||||||
|
if (action.payload.every((change) => change.type === 'select')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const individualGroupByMatcher = isAnyOf(nodesChanged, viewportChanged);
|
||||||
|
|
||||||
|
export const nodesUndoableConfig: UndoableOptions<NodesState, UnknownAction> = {
|
||||||
|
limit: 64,
|
||||||
|
undoType: nodesSlice.actions.undo.type,
|
||||||
|
redoType: nodesSlice.actions.redo.type,
|
||||||
|
groupBy: (action, state, history) => {
|
||||||
|
if (isSelectionAction(action)) {
|
||||||
|
// Changes to selection should never be recorded on their own
|
||||||
|
return history.group;
|
||||||
|
}
|
||||||
|
if (individualGroupByMatcher(action)) {
|
||||||
|
return action.type;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
filter: (action, _state, _history) => {
|
||||||
|
if (nodesChanged.match(action)) {
|
||||||
|
if (action.payload.every((change) => change.type === 'dimensions')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// This is used for tracking `state.workflow.isTouched`
|
// This is used for tracking `state.workflow.isTouched`
|
||||||
export const isAnyNodeOrEdgeMutation = isAnyOf(
|
export const isAnyNodeOrEdgeMutation = isAnyOf(
|
||||||
connectionMade,
|
connectionMade,
|
||||||
@ -636,47 +698,3 @@ export const isAnyNodeOrEdgeMutation = isAnyOf(
|
|||||||
selectionPasted,
|
selectionPasted,
|
||||||
edgeAdded
|
edgeAdded
|
||||||
);
|
);
|
||||||
|
|
||||||
export const $cursorPos = atom<XYPosition | null>(null);
|
|
||||||
export const $templates = atom<Templates>({});
|
|
||||||
export const $copiedNodes = atom<AnyNode[]>([]);
|
|
||||||
export const $copiedEdges = atom<InvocationNodeEdge[]>([]);
|
|
||||||
export const $pendingConnection = atom<PendingConnection | null>(null);
|
|
||||||
export const $isModifyingEdge = atom(false);
|
|
||||||
export const $isAddNodePopoverOpen = atom(false);
|
|
||||||
export const closeAddNodePopover = () => {
|
|
||||||
$isAddNodePopoverOpen.set(false);
|
|
||||||
$pendingConnection.set(null);
|
|
||||||
};
|
|
||||||
export const openAddNodePopover = () => {
|
|
||||||
$isAddNodePopoverOpen.set(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const selectNodesSlice = (state: RootState) => state.nodes.present;
|
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
||||||
const migrateNodesState = (state: any): any => {
|
|
||||||
if (!('_version' in state)) {
|
|
||||||
state._version = 1;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const nodesPersistConfig: PersistConfig<NodesState> = {
|
|
||||||
name: nodesSlice.name,
|
|
||||||
initialState: initialNodesState,
|
|
||||||
migrate: migrateNodesState,
|
|
||||||
persistDenylist: ['selectedNodes', 'selectedEdges'],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const nodesUndoableConfig: UndoableOptions<NodesState, UnknownAction> = {
|
|
||||||
limit: 64,
|
|
||||||
undoType: nodesSlice.actions.undo.type,
|
|
||||||
redoType: nodesSlice.actions.redo.type,
|
|
||||||
groupBy: (action, state, history) => {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
filter: (action, _state, _history) => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
@ -28,3 +28,11 @@ export const selectFieldInputInstance = (
|
|||||||
const data = selectNodeData(nodesSlice, nodeId);
|
const data = selectNodeData(nodesSlice, nodeId);
|
||||||
return data?.inputs[fieldName] ?? null;
|
return data?.inputs[fieldName] ?? null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const selectLastSelectedNode = (nodesSlice: NodesState) => {
|
||||||
|
const selectedNodes = nodesSlice.nodes.filter((n) => n.selected);
|
||||||
|
if (selectedNodes.length === 1) {
|
||||||
|
return selectedNodes[0];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
@ -26,8 +26,6 @@ export type NodesState = {
|
|||||||
_version: 1;
|
_version: 1;
|
||||||
nodes: AnyNode[];
|
nodes: AnyNode[];
|
||||||
edges: InvocationNodeEdge[];
|
edges: InvocationNodeEdge[];
|
||||||
selectedNodes: string[];
|
|
||||||
selectedEdges: string[];
|
|
||||||
nodeExecutionStates: Record<string, NodeExecutionState>;
|
nodeExecutionStates: Record<string, NodeExecutionState>;
|
||||||
viewport: Viewport;
|
viewport: Viewport;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user