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,
|
||||
redo,
|
||||
selectedAll,
|
||||
selectedEdgesChanged,
|
||||
selectedNodesChanged,
|
||||
undo,
|
||||
viewportChanged,
|
||||
} from 'features/nodes/store/nodesSlice';
|
||||
@ -32,7 +30,6 @@ import type {
|
||||
OnMoveEnd,
|
||||
OnNodesChange,
|
||||
OnNodesDelete,
|
||||
OnSelectionChangeFunc,
|
||||
ProOptions,
|
||||
ReactFlowProps,
|
||||
} from 'reactflow';
|
||||
@ -111,14 +108,6 @@ export const Flow = memo(() => {
|
||||
[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(
|
||||
(e, viewport) => {
|
||||
dispatch(viewportChanged(viewport));
|
||||
@ -258,7 +247,6 @@ export const Flow = memo(() => {
|
||||
onConnectEnd={onConnectEnd}
|
||||
onMoveEnd={handleMoveEnd}
|
||||
connectionLineComponent={CustomConnectionLine}
|
||||
onSelectionChange={handleSelectionChange}
|
||||
isValidConnection={isValidConnection}
|
||||
minZoom={0.1}
|
||||
snapToGrid={shouldSnapToGrid}
|
||||
|
@ -3,27 +3,21 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||
import { selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||
import { memo } from 'react';
|
||||
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);
|
||||
|
||||
return {
|
||||
data: lastSelectedNode?.data,
|
||||
};
|
||||
});
|
||||
const selector = createMemoizedSelector(selectNodesSlice, (nodes) => selectLastSelectedNode(nodes));
|
||||
|
||||
const InspectorDataTab = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data } = useAppSelector(selector);
|
||||
const lastSelectedNode = useAppSelector(selector);
|
||||
|
||||
if (!data) {
|
||||
if (!lastSelectedNode) {
|
||||
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);
|
||||
|
@ -7,6 +7,7 @@ import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableCon
|
||||
import NotesTextarea from 'features/nodes/components/flow/nodes/Invocation/NotesTextarea';
|
||||
import { useNodeNeedsUpdate } from 'features/nodes/hooks/useNodeNeedsUpdate';
|
||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -18,8 +19,7 @@ const InspectorDetailsTab = () => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
||||
const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
||||
const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId);
|
||||
const lastSelectedNode = selectLastSelectedNode(nodes);
|
||||
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
||||
|
||||
if (!isInvocationNode(lastSelectedNode) || !lastSelectedNodeTemplate) {
|
||||
|
@ -6,6 +6,7 @@ import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -19,11 +20,10 @@ const InspectorOutputsTab = () => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
||||
const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
||||
const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId);
|
||||
const lastSelectedNode = selectLastSelectedNode(nodes);
|
||||
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) {
|
||||
return;
|
||||
|
@ -4,6 +4,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||
import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice';
|
||||
import { selectLastSelectedNode } from 'features/nodes/store/selectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@ -12,8 +13,7 @@ const NodeTemplateInspector = () => {
|
||||
const selector = useMemo(
|
||||
() =>
|
||||
createMemoizedSelector(selectNodesSlice, (nodes) => {
|
||||
const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1];
|
||||
const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId);
|
||||
const lastSelectedNode = selectLastSelectedNode(nodes);
|
||||
const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined;
|
||||
|
||||
return lastSelectedNodeTemplate;
|
||||
|
@ -74,8 +74,6 @@ const initialNodesState: NodesState = {
|
||||
_version: 1,
|
||||
nodes: [],
|
||||
edges: [],
|
||||
selectedNodes: [],
|
||||
selectedEdges: [],
|
||||
nodeExecutionStates: {},
|
||||
viewport: { x: 0, y: 0, zoom: 1 },
|
||||
};
|
||||
@ -351,12 +349,6 @@ export const nodesSlice = createSlice({
|
||||
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>) => {
|
||||
fieldValueReducer(state, action, zStatefulFieldValue);
|
||||
},
|
||||
@ -593,8 +585,6 @@ export const {
|
||||
nodeUseCacheChanged,
|
||||
notesNodeValueChanged,
|
||||
selectedAll,
|
||||
selectedEdgesChanged,
|
||||
selectedNodesChanged,
|
||||
selectionPasted,
|
||||
viewportChanged,
|
||||
edgeAdded,
|
||||
@ -602,6 +592,78 @@ export const {
|
||||
redo,
|
||||
} = 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`
|
||||
export const isAnyNodeOrEdgeMutation = isAnyOf(
|
||||
connectionMade,
|
||||
@ -636,47 +698,3 @@ export const isAnyNodeOrEdgeMutation = isAnyOf(
|
||||
selectionPasted,
|
||||
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);
|
||||
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;
|
||||
nodes: AnyNode[];
|
||||
edges: InvocationNodeEdge[];
|
||||
selectedNodes: string[];
|
||||
selectedEdges: string[];
|
||||
nodeExecutionStates: Record<string, NodeExecutionState>;
|
||||
viewport: Viewport;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user