From 7f78fe7a36fac8c97f3b23c52a26f3bb09cc0d13 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 16 May 2024 21:21:39 +1000 Subject: [PATCH] feat(ui): move viewport state to nanostores --- .../features/dnd/hooks/useScaledCenteredModifer.ts | 14 ++++++-------- .../src/features/nodes/components/flow/Flow.tsx | 14 ++++++-------- .../web/src/features/nodes/store/nodesSlice.ts | 8 ++------ .../frontend/web/src/features/nodes/store/types.ts | 2 -- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts index f3f0c50f03..ce93611ff4 100644 --- a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts +++ b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts @@ -1,23 +1,21 @@ import type { Modifier } from '@dnd-kit/core'; import { getEventCoordinates } from '@dnd-kit/utilities'; -import { createSelector } from '@reduxjs/toolkit'; +import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $viewport } from 'features/nodes/store/nodesSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { useCallback } from 'react'; -const selectZoom = createSelector([selectNodesSlice, activeTabNameSelector], (nodes, activeTabName) => - activeTabName === 'workflows' ? nodes.viewport.zoom : 1 -); - /** * Applies scaling to the drag transform (if on node editor tab) and centers it on cursor. */ export const useScaledModifer = () => { - const zoom = useAppSelector(selectZoom); + const activeTabName = useAppSelector(activeTabNameSelector); + const workflowsViewport = useStore($viewport); const modifier: Modifier = useCallback( ({ activatorEvent, draggingNodeRect, transform }) => { if (draggingNodeRect && activatorEvent) { + const zoom = activeTabName === 'workflows' ? workflowsViewport.zoom : 1; const activatorCoordinates = getEventCoordinates(activatorEvent); if (!activatorCoordinates) { @@ -42,7 +40,7 @@ export const useScaledModifer = () => { return transform; }, - [zoom] + [activeTabName, workflowsViewport.zoom] ); return modifier; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx index dd10eb8cba..177abd57f5 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx @@ -1,4 +1,5 @@ import { useGlobalMenuClose, useToken } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useConnection } from 'features/nodes/hooks/useConnection'; import { useCopyPaste } from 'features/nodes/hooks/useCopyPaste'; @@ -6,6 +7,7 @@ import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection' import { useWorkflowWatcher } from 'features/nodes/hooks/useWorkflowWatcher'; import { $cursorPos, + $viewport, connectionMade, edgeAdded, edgeDeleted, @@ -16,7 +18,6 @@ import { redo, selectedAll, undo, - viewportChanged, } from 'features/nodes/store/nodesSlice'; import { $flow } from 'features/nodes/store/reactFlowInstance'; import type { CSSProperties, MouseEvent } from 'react'; @@ -64,7 +65,7 @@ export const Flow = memo(() => { const dispatch = useAppDispatch(); const nodes = useAppSelector((s) => s.nodes.present.nodes); const edges = useAppSelector((s) => s.nodes.present.edges); - const viewport = useAppSelector((s) => s.nodes.present.viewport); + const viewport = useStore($viewport); const shouldSnapToGrid = useAppSelector((s) => s.workflowSettings.shouldSnapToGrid); const selectionMode = useAppSelector((s) => s.workflowSettings.selectionMode); const { onConnectStart, onConnect, onConnectEnd } = useConnection(); @@ -108,12 +109,9 @@ export const Flow = memo(() => { [dispatch] ); - const handleMoveEnd: OnMoveEnd = useCallback( - (e, viewport) => { - dispatch(viewportChanged(viewport)); - }, - [dispatch] - ); + const handleMoveEnd: OnMoveEnd = useCallback((e, viewport) => { + $viewport.set(viewport); + }, []); const { onCloseGlobal } = useGlobalMenuClose(); const handlePaneClick = useCallback(() => { diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index 47bf3a5ab1..cc3c9db3e8 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -75,7 +75,6 @@ const initialNodesState: NodesState = { nodes: [], edges: [], nodeExecutionStates: {}, - viewport: { x: 0, y: 0, zoom: 1 }, }; type FieldValueAction = PayloadAction<{ @@ -410,9 +409,6 @@ export const nodesSlice = createSlice({ state.nodes = []; state.edges = []; }, - viewportChanged: (state, action: PayloadAction) => { - state.viewport = action.payload; - }, selectedAll: (state) => { state.nodes = applyNodeChanges( state.nodes.map((n) => ({ id: n.id, type: 'select', selected: true })), @@ -586,7 +582,6 @@ export const { notesNodeValueChanged, selectedAll, selectionPasted, - viewportChanged, edgeAdded, undo, redo, @@ -598,6 +593,7 @@ export const $copiedNodes = atom([]); export const $copiedEdges = atom([]); export const $pendingConnection = atom(null); export const $isModifyingEdge = atom(false); +export const $viewport = atom({ x: 0, y: 0, zoom: 1 }); export const $isAddNodePopoverOpen = atom(false); export const closeAddNodePopover = () => { $isAddNodePopoverOpen.set(false); @@ -638,7 +634,7 @@ const isSelectionAction = (action: UnknownAction) => { return false; }; -const individualGroupByMatcher = isAnyOf(nodesChanged, viewportChanged); +const individualGroupByMatcher = isAnyOf(nodesChanged); export const nodesUndoableConfig: UndoableOptions = { limit: 64, diff --git a/invokeai/frontend/web/src/features/nodes/store/types.ts b/invokeai/frontend/web/src/features/nodes/store/types.ts index 47e9ecaf85..090a967626 100644 --- a/invokeai/frontend/web/src/features/nodes/store/types.ts +++ b/invokeai/frontend/web/src/features/nodes/store/types.ts @@ -12,7 +12,6 @@ import type { NodeExecutionState, } from 'features/nodes/types/invocation'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; -import type { Viewport } from 'reactflow'; export type Templates = Record; @@ -27,7 +26,6 @@ export type NodesState = { nodes: AnyNode[]; edges: InvocationNodeEdge[]; nodeExecutionStates: Record; - viewport: Viewport; }; export type WorkflowMode = 'edit' | 'view';