diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index e3a7f3702a..cacea30e48 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -28,7 +28,6 @@ import { generationPersistConfig, generationSlice } from 'features/parameters/st import { upscalePersistConfig, upscaleSlice } from 'features/parameters/store/upscaleSlice'; import { queueSlice } from 'features/queue/store/queueSlice'; import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice'; -import { stylePresetModalSlice } from 'features/stylePresets/store/stylePresetModalSlice'; import { stylePresetPersistConfig, stylePresetSlice } from 'features/stylePresets/store/stylePresetSlice'; import { configSlice } from 'features/system/store/configSlice'; import { systemPersistConfig, systemSlice } from 'features/system/store/systemSlice'; @@ -71,7 +70,6 @@ const allReducers = { [workflowSettingsSlice.name]: workflowSettingsSlice.reducer, [api.reducerPath]: api.reducer, [upscaleSlice.name]: upscaleSlice.reducer, - [stylePresetModalSlice.name]: stylePresetModalSlice.reducer, [stylePresetSlice.name]: stylePresetSlice.reducer, }; diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts b/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts index b6086f846f..e39d7816a4 100644 --- a/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts +++ b/invokeai/frontend/web/src/features/gallery/hooks/useImageActions.ts @@ -1,7 +1,7 @@ import { skipToken } from '@reduxjs/toolkit/query'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useAppSelector } from 'app/store/storeHooks'; import { handlers, parseAndRecallAllMetadata, parseAndRecallPrompts } from 'features/metadata/util/handlers'; -import { isModalOpenChanged, prefilledFormDataChanged } from 'features/stylePresets/store/stylePresetModalSlice'; +import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { useCallback, useEffect, useState } from 'react'; import { useGetImageDTOQuery } from 'services/api/endpoints/images'; @@ -13,7 +13,6 @@ export const useImageActions = (image_name?: string) => { const [hasMetadata, setHasMetadata] = useState(false); const [hasSeed, setHasSeed] = useState(false); const [hasPrompts, setHasPrompts] = useState(false); - const dispatch = useAppDispatch(); const { data: imageDTO } = useGetImageDTOQuery(image_name ?? skipToken); useEffect(() => { @@ -71,17 +70,18 @@ export const useImageActions = (image_name?: string) => { const positivePrompt = await handlers.positivePrompt.parse(metadata); const negativePrompt = await handlers.negativePrompt.parse(metadata); - dispatch( - prefilledFormDataChanged({ + $stylePresetModalState.set({ + prefilledFormData: { name: '', positivePrompt, negativePrompt, imageUrl: imageDTO.image_url, - }) - ); - dispatch(isModalOpenChanged(true)); + }, + updatingStylePresetId: null, + isModalOpen: true, + }); } - }, [image_name, metadata, dispatch, imageDTO]); + }, [image_name, metadata, imageDTO]); return { recallAll, diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx index 74248ff77a..12abd47492 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx @@ -1,10 +1,5 @@ import { Button, Flex, FormControl, FormLabel, Input, Text } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { - isModalOpenChanged, - prefilledFormDataChanged, - updatingStylePresetIdChanged, -} from 'features/stylePresets/store/stylePresetModalSlice'; +import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal'; import { toast } from 'features/toast/toast'; import { useCallback } from 'react'; import type { SubmitHandler } from 'react-hook-form'; @@ -31,7 +26,6 @@ export const StylePresetForm = ({ }) => { const [createStylePreset] = useCreateStylePresetMutation(); const [updateStylePreset] = useUpdateStylePresetMutation(); - const dispatch = useAppDispatch(); const { t } = useTranslation(); const { handleSubmit, control, register, formState } = useForm({ @@ -69,11 +63,13 @@ export const StylePresetForm = ({ }); } - dispatch(prefilledFormDataChanged(null)); - dispatch(updatingStylePresetIdChanged(null)); - dispatch(isModalOpenChanged(false)); + $stylePresetModalState.set({ + prefilledFormData: null, + updatingStylePresetId: null, + isModalOpen: false, + }); }, - [dispatch, updatingStylePresetId, updateStylePreset, createStylePreset] + [updatingStylePresetId, updateStylePreset, createStylePreset] ); return ( diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx index fe41372f73..e1c1b14e22 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx @@ -1,11 +1,7 @@ import { Badge, ConfirmationAlertDialog, Flex, IconButton, Text, useDisclosure } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { $isMenuOpen } from 'features/stylePresets/store/isMenuOpen'; -import { - isModalOpenChanged, - prefilledFormDataChanged, - updatingStylePresetIdChanged, -} from 'features/stylePresets/store/stylePresetModalSlice'; +import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal'; import { activeStylePresetIdChanged } from 'features/stylePresets/store/stylePresetSlice'; import { toast } from 'features/toast/toast'; import type { MouseEvent } from 'react'; @@ -30,19 +26,18 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI const { name, preset_data } = preset; const { positive_prompt, negative_prompt } = preset_data; - dispatch( - prefilledFormDataChanged({ + $stylePresetModalState.set({ + prefilledFormData: { name, positivePrompt: positive_prompt || '', negativePrompt: negative_prompt || '', imageUrl: preset.image, - }) - ); - - dispatch(updatingStylePresetIdChanged(preset.id)); - dispatch(isModalOpenChanged(true)); + }, + updatingStylePresetId: preset.id, + isModalOpen: true, + }); }, - [dispatch, preset] + [preset] ); const handleClickApply = useCallback(async () => { @@ -64,19 +59,18 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI const { name, preset_data } = preset; const { positive_prompt, negative_prompt } = preset_data; - dispatch( - prefilledFormDataChanged({ + $stylePresetModalState.set({ + prefilledFormData: { name: `${name} (${t('common.copy')})`, positivePrompt: positive_prompt || '', negativePrompt: negative_prompt || '', imageUrl: preset.image, - }) - ); - - dispatch(updatingStylePresetIdChanged(null)); - dispatch(isModalOpenChanged(true)); + }, + updatingStylePresetId: null, + isModalOpen: true, + }); }, - [dispatch, preset, t] + [preset, t] ); const handleDeletePreset = useCallback(async () => { diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx index 5bd4ba5ef2..d9631ba6b4 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx @@ -1,11 +1,7 @@ import { Flex, IconButton, Text } from '@invoke-ai/ui-library'; import { EMPTY_ARRAY } from 'app/store/constants'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { - isModalOpenChanged, - prefilledFormDataChanged, - updatingStylePresetIdChanged, -} from 'features/stylePresets/store/stylePresetModalSlice'; +import { useAppSelector } from 'app/store/storeHooks'; +import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { PiPlusBold } from 'react-icons/pi'; @@ -40,14 +36,15 @@ export const StylePresetMenu = () => { }, }); - const dispatch = useAppDispatch(); const { t } = useTranslation(); const handleClickAddNew = useCallback(() => { - dispatch(prefilledFormDataChanged(null)); - dispatch(updatingStylePresetIdChanged(null)); - dispatch(isModalOpenChanged(true)); - }, [dispatch]); + $stylePresetModalState.set({ + prefilledFormData: null, + updatingStylePresetId: null, + isModalOpen: true, + }); + }, []); return ( diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx index 4865aa2483..de8cfae062 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx @@ -8,13 +8,9 @@ import { ModalOverlay, Spinner, } from '@invoke-ai/ui-library'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useStore } from '@nanostores/react'; import { convertImageUrlToBlob } from 'common/util/convertImageUrlToBlob'; -import { - isModalOpenChanged, - prefilledFormDataChanged, - updatingStylePresetIdChanged, -} from 'features/stylePresets/store/stylePresetModalSlice'; +import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal'; import type { PrefilledFormData } from 'features/stylePresets/store/types'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -24,21 +20,22 @@ import { StylePresetForm } from './StylePresetForm'; export const StylePresetModal = () => { const [formData, setFormData] = useState(null); - const dispatch = useAppDispatch(); const { t } = useTranslation(); - const isModalOpen = useAppSelector((s) => s.stylePresetModal.isModalOpen); - const updatingStylePresetId = useAppSelector((s) => s.stylePresetModal.updatingStylePresetId); - const prefilledFormData = useAppSelector((s) => s.stylePresetModal.prefilledFormData); + const stylePresetModalState = useStore($stylePresetModalState); const modalTitle = useMemo(() => { - return updatingStylePresetId ? t('stylePresets.updatePromptTemplate') : t('stylePresets.createPromptTemplate'); - }, [updatingStylePresetId, t]); + return stylePresetModalState.updatingStylePresetId + ? t('stylePresets.updatePromptTemplate') + : t('stylePresets.createPromptTemplate'); + }, [stylePresetModalState.updatingStylePresetId, t]); const handleCloseModal = useCallback(() => { - dispatch(prefilledFormDataChanged(null)); - dispatch(updatingStylePresetIdChanged(null)); - dispatch(isModalOpenChanged(false)); - }, [dispatch]); + $stylePresetModalState.set({ + prefilledFormData: null, + updatingStylePresetId: null, + isModalOpen: false, + }); + }, []); useEffect(() => { setFormData(null); @@ -62,18 +59,18 @@ export const StylePresetModal = () => { }); } }; - convertImageToBlob(prefilledFormData); - }, [prefilledFormData]); + convertImageToBlob(stylePresetModalState.prefilledFormData); + }, [stylePresetModalState.prefilledFormData]); return ( - + {modalTitle} - {!prefilledFormData || formData ? ( - + {!stylePresetModalState.prefilledFormData || formData ? ( + ) : ( )} diff --git a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModal.ts b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModal.ts new file mode 100644 index 0000000000..14f78f8b22 --- /dev/null +++ b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModal.ts @@ -0,0 +1,25 @@ +import { atom } from 'nanostores'; + +const initialState: StylePresetModalState = { + isModalOpen: false, + updatingStylePresetId: null, + prefilledFormData: null, +}; + +/** + * Tracks the state for the style preset modal. + */ +export const $stylePresetModalState = atom(initialState); + +export type StylePresetModalState = { + isModalOpen: boolean; + updatingStylePresetId: string | null; + prefilledFormData: PrefilledFormData | null; +}; + +export type PrefilledFormData = { + name: string; + positivePrompt: string; + negativePrompt: string; + imageUrl: string | null; +}; diff --git a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModalSlice.ts b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModalSlice.ts deleted file mode 100644 index 3afde686c8..0000000000 --- a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModalSlice.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { PayloadAction } from '@reduxjs/toolkit'; -import { createSlice } from '@reduxjs/toolkit'; - -import type { PrefilledFormData, StylePresetModalState } from './types'; - -const initialState: StylePresetModalState = { - isModalOpen: false, - updatingStylePresetId: null, - prefilledFormData: null, -}; - -export const stylePresetModalSlice = createSlice({ - name: 'stylePresetModal', - initialState: initialState, - reducers: { - isModalOpenChanged: (state, action: PayloadAction) => { - state.isModalOpen = action.payload; - }, - updatingStylePresetIdChanged: (state, action: PayloadAction) => { - state.updatingStylePresetId = action.payload; - }, - prefilledFormDataChanged: (state, action: PayloadAction) => { - state.prefilledFormData = action.payload; - }, - }, -}); - -export const { isModalOpenChanged, updatingStylePresetIdChanged, prefilledFormDataChanged } = - stylePresetModalSlice.actions;