From c7dcf1f4a00e8491f6cf2f9be0a9330b5c8e32b0 Mon Sep 17 00:00:00 2001 From: mickr777 <115216705+mickr777@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:40:09 +1000 Subject: [PATCH 1/7] Create ClearNodesButton.tsx --- .../nodes/components/ui/ClearNodesButton.tsx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx new file mode 100644 index 0000000000..f9824268b9 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx @@ -0,0 +1,41 @@ +import { useAppDispatch } from 'app/store/storeHooks'; +import { clearNodes } from 'features/nodes/store/nodesSlice'; +import { makeToast } from 'app/components/Toaster'; +import { addToast } from 'features/system/store/systemSlice'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FaTrash } from 'react-icons/fa'; +import IAIIconButton from 'common/components/IAIIconButton'; + +const ClearNodesButton = () => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + + const handleClearNodes = () => { + const confirmed = window.confirm(t('common.clearNodes')); + + if (confirmed) { + dispatch(clearNodes()); + + dispatch( + addToast( + makeToast({ + title: t('toast.nodesCleared'), + status: 'success', + }) + ) + ); + } + }; + + return ( + } + tooltip={t('nodes.clearNodes')} + aria-label={t('nodes.clearNodes')} + onClick={handleClearNodes} + /> + ); +}; + +export default memo(ClearNodesButton); From 99c1d5c044d542a7b7c980f954be1af05c75b75c Mon Sep 17 00:00:00 2001 From: mickr777 <115216705+mickr777@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:40:33 +1000 Subject: [PATCH 2/7] Update nodesSlice.ts --- invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index 094a43b944..b2825db783 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -133,6 +133,10 @@ const nodesSlice = createSlice({ loadFileEdges: (state, action: PayloadAction) => { state.edges = action.payload; }, + clearNodes: (state) => { + state.nodes = []; + state.edges = []; + }, }, extraReducers: (builder) => { builder.addCase(receivedOpenAPISchema.fulfilled, (state, action) => { @@ -156,6 +160,7 @@ export const { setEditorInstance, loadFileNodes, loadFileEdges, + clearNodes, } = nodesSlice.actions; export default nodesSlice.reducer; From 90441c425709bb327fa5d22a684e602fec506db5 Mon Sep 17 00:00:00 2001 From: mickr777 <115216705+mickr777@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:41:00 +1000 Subject: [PATCH 3/7] Update TopCenterPanel.tsx --- .../web/src/features/nodes/components/panels/TopCenterPanel.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/invokeai/frontend/web/src/features/nodes/components/panels/TopCenterPanel.tsx b/invokeai/frontend/web/src/features/nodes/components/panels/TopCenterPanel.tsx index 10e59f2af9..90f8039285 100644 --- a/invokeai/frontend/web/src/features/nodes/components/panels/TopCenterPanel.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/panels/TopCenterPanel.tsx @@ -6,6 +6,7 @@ import LoadNodesButton from '../ui/LoadNodesButton'; import NodeInvokeButton from '../ui/NodeInvokeButton'; import ReloadSchemaButton from '../ui/ReloadSchemaButton'; import SaveNodesButton from '../ui/SaveNodesButton'; +import ClearNodesButton from '../ui/ClearNodesButton'; const TopCenterPanel = () => { return ( @@ -16,6 +17,7 @@ const TopCenterPanel = () => { + ); From 8a25e22fb0b60e651f8b7463270c3d51a7fc1a08 Mon Sep 17 00:00:00 2001 From: mickr777 <115216705+mickr777@users.noreply.github.com> Date: Thu, 13 Jul 2023 14:42:09 +1000 Subject: [PATCH 4/7] Update en.json --- invokeai/frontend/web/public/locales/en.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index b198f27160..a6a516c380 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -102,7 +102,8 @@ "openInNewTab": "Open in New Tab", "dontAskMeAgain": "Don't ask me again", "areYouSure": "Are you sure?", - "imagePrompt": "Image Prompt" + "imagePrompt": "Image Prompt", + "clearNodes": "Are you sure you want to clear all nodes?" }, "gallery": { "generations": "Generations", @@ -596,7 +597,9 @@ "initialImageNotSetDesc": "Could not load initial image", "nodesSaved": "Nodes Saved", "nodesLoaded": "Nodes Loaded", - "nodesLoadedFailed": "Failed To Load Nodes" + "nodesLoadedFailed": "Failed To Load Nodes", + "nodesCleared": "Nodes Cleared" + }, "tooltip": { "feature": { @@ -681,6 +684,7 @@ "nodes": { "reloadSchema": "Reload Schema", "saveNodes": "Save Nodes", - "loadNodes": "Load Nodes" + "loadNodes": "Load Nodes", + "clearNodes": "Clear Nodes" } } From d1ac50b0619a58b269430a7423b3b6d3e0681a1e Mon Sep 17 00:00:00 2001 From: mickr777 <115216705+mickr777@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:19:59 +1000 Subject: [PATCH 5/7] Change Confirmation Dialog Changed Confirmation Dialog to use chakra-ui alert dialog --- .../nodes/components/ui/ClearNodesButton.tsx | 85 +++++++++++++++---- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx index f9824268b9..29e8ca3a20 100644 --- a/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx @@ -1,3 +1,15 @@ +import { useState } from 'react'; +import { + AlertDialog, + AlertDialogBody, + AlertDialogContent, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, + Divider, + Text, + Button, +} from '@chakra-ui/react'; import { useAppDispatch } from 'app/store/storeHooks'; import { clearNodes } from 'features/nodes/store/nodesSlice'; import { makeToast } from 'app/components/Toaster'; @@ -10,31 +22,68 @@ import IAIIconButton from 'common/components/IAIIconButton'; const ClearNodesButton = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); + const [isDialogOpen, setIsDialogOpen] = useState(false); const handleClearNodes = () => { - const confirmed = window.confirm(t('common.clearNodes')); + setIsDialogOpen(true); + }; - if (confirmed) { - dispatch(clearNodes()); + const handleConfirmClear = () => { + dispatch(clearNodes()); - dispatch( - addToast( - makeToast({ - title: t('toast.nodesCleared'), - status: 'success', - }) - ) - ); - } + dispatch( + addToast( + makeToast({ + title: t('toast.nodesCleared'), + status: 'success', + }) + ) + ); + + setIsDialogOpen(false); + }; + + const handleCancelClear = () => { + setIsDialogOpen(false); }; return ( - } - tooltip={t('nodes.clearNodes')} - aria-label={t('nodes.clearNodes')} - onClick={handleClearNodes} - /> + <> + } + tooltip={t('nodes.clearNodes')} + aria-label={t('nodes.clearNodes')} + onClick={handleClearNodes} + /> + + + + + + + {t('nodes.clearNodes')} + + + + {t('common.clearNodes')} + + + + + + + + + + + ); }; From 91c4e4c9b98ed9a3f765987ab58b25451ae3a1d9 Mon Sep 17 00:00:00 2001 From: mickr777 <115216705+mickr777@users.noreply.github.com> Date: Thu, 13 Jul 2023 18:08:30 +1000 Subject: [PATCH 6/7] useDisclosure instead of useState. --- .../nodes/components/ui/ClearNodesButton.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx index 29e8ca3a20..78f3e2af2b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useDisclosure } from '@chakra-ui/react'; import { AlertDialog, AlertDialogBody, @@ -22,10 +22,10 @@ import IAIIconButton from 'common/components/IAIIconButton'; const ClearNodesButton = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const [isDialogOpen, setIsDialogOpen] = useState(false); + const { isOpen, onOpen, onClose } = useDisclosure(); const handleClearNodes = () => { - setIsDialogOpen(true); + onOpen(); }; const handleConfirmClear = () => { @@ -40,11 +40,11 @@ const ClearNodesButton = () => { ) ); - setIsDialogOpen(false); + onClose(); }; const handleCancelClear = () => { - setIsDialogOpen(false); + onClose(); }; return ( @@ -56,12 +56,7 @@ const ClearNodesButton = () => { onClick={handleClearNodes} /> - + From 686149969714296196c692e79d36a86be07556d5 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Thu, 13 Jul 2023 20:34:23 +1200 Subject: [PATCH 7/7] fix: Simplify modal code --- .../nodes/components/ui/ClearNodesButton.tsx | 39 ++++++++++--------- .../nodes/components/ui/SaveNodesButton.tsx | 3 ++ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx index 78f3e2af2b..22845dd362 100644 --- a/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/ui/ClearNodesButton.tsx @@ -1,4 +1,3 @@ -import { useDisclosure } from '@chakra-ui/react'; import { AlertDialog, AlertDialogBody, @@ -6,27 +5,27 @@ import { AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, - Divider, - Text, Button, + Text, + useDisclosure, } from '@chakra-ui/react'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { clearNodes } from 'features/nodes/store/nodesSlice'; import { makeToast } from 'app/components/Toaster'; +import { RootState } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import IAIIconButton from 'common/components/IAIIconButton'; +import { clearNodes } from 'features/nodes/store/nodesSlice'; import { addToast } from 'features/system/store/systemSlice'; -import { memo } from 'react'; +import { memo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { FaTrash } from 'react-icons/fa'; -import IAIIconButton from 'common/components/IAIIconButton'; const ClearNodesButton = () => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { isOpen, onOpen, onClose } = useDisclosure(); + const cancelRef = useRef(null); - const handleClearNodes = () => { - onOpen(); - }; + const nodes = useAppSelector((state: RootState) => state.nodes.nodes); const handleConfirmClear = () => { dispatch(clearNodes()); @@ -43,20 +42,22 @@ const ClearNodesButton = () => { onClose(); }; - const handleCancelClear = () => { - onClose(); - }; - return ( <> } tooltip={t('nodes.clearNodes')} aria-label={t('nodes.clearNodes')} - onClick={handleClearNodes} + onClick={onOpen} + isDisabled={nodes.length === 0} /> - + @@ -68,10 +69,10 @@ const ClearNodesButton = () => { {t('common.clearNodes')} - - - + diff --git a/invokeai/frontend/web/src/features/nodes/components/ui/SaveNodesButton.tsx b/invokeai/frontend/web/src/features/nodes/components/ui/SaveNodesButton.tsx index 14bf0a1ce8..5833182456 100644 --- a/invokeai/frontend/web/src/features/nodes/components/ui/SaveNodesButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/ui/SaveNodesButton.tsx @@ -12,6 +12,8 @@ const SaveNodesButton = () => { (state: RootState) => state.nodes.editorInstance ); + const nodes = useAppSelector((state: RootState) => state.nodes.nodes); + const saveEditorToJSON = useCallback(() => { if (editorInstance) { const editorState = editorInstance.toObject(); @@ -38,6 +40,7 @@ const SaveNodesButton = () => { tooltip={t('nodes.saveNodes')} aria-label={t('nodes.saveNodes')} onClick={saveEditorToJSON} + isDisabled={nodes.length === 0} /> ); };