diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index aa1919abfb..8c033440e3 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -12,33 +12,26 @@ import { languageSelector } from 'features/system/store/systemSelectors'; import InvokeTabs from 'features/ui/components/InvokeTabs'; import i18n from 'i18n'; import { size } from 'lodash-es'; -import { ReactNode, memo, useCallback, useEffect } from 'react'; +import { memo, useCallback, useEffect } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { usePreselectedImage } from '../../features/parameters/hooks/usePreselectedImage'; import AppErrorBoundaryFallback from './AppErrorBoundaryFallback'; import GlobalHotkeys from './GlobalHotkeys'; import Toaster from './Toaster'; -import { CustomStarUi } from '../../features/ui/store/uiTypes'; -import { setCustomStarUi } from '../../features/ui/store/uiSlice'; +import { useStore } from '@nanostores/react'; +import { $headerComponent } from 'app/store/nanostores/headerComponent'; const DEFAULT_CONFIG = {}; interface Props { config?: PartialAppConfig; - headerComponent?: ReactNode; selectedImage?: { imageName: string; action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters'; }; - customStarUi?: CustomStarUi; } -const App = ({ - config = DEFAULT_CONFIG, - headerComponent, - selectedImage, - customStarUi, -}: Props) => { +const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => { const language = useAppSelector(languageSelector); const logger = useLogger('system'); @@ -61,12 +54,6 @@ const App = ({ } }, [dispatch, config, logger]); - useEffect(() => { - if (customStarUi) { - dispatch(setCustomStarUi(customStarUi)); - } - }, [customStarUi, dispatch]); - useEffect(() => { dispatch(appStarted()); }, [dispatch]); @@ -75,6 +62,8 @@ const App = ({ handlePreselectedImage(selectedImage); }, [handlePreselectedImage, selectedImage]); + const headerComponent = useStore($headerComponent); + return ( import('./App')); const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider')); @@ -83,18 +84,33 @@ const InvokeAIUI = ({ }; }, [apiUrl, token, middleware, projectId]); + useEffect(() => { + if (customStarUi) { + $customStarUI.set(customStarUi); + } + + return () => { + $customStarUI.set(undefined); + }; + }, [customStarUi]); + + useEffect(() => { + if (headerComponent) { + $headerComponent.set(headerComponent); + } + + return () => { + $headerComponent.set(undefined); + }; + }, [headerComponent]); + return ( }> - + diff --git a/invokeai/frontend/web/src/app/store/nanostores/customStarUI.ts b/invokeai/frontend/web/src/app/store/nanostores/customStarUI.ts new file mode 100644 index 0000000000..0459c2f31f --- /dev/null +++ b/invokeai/frontend/web/src/app/store/nanostores/customStarUI.ts @@ -0,0 +1,14 @@ +import { MenuItemProps } from '@chakra-ui/react'; +import { atom } from 'nanostores'; + +export type CustomStarUi = { + on: { + icon: MenuItemProps['icon']; + text: string; + }; + off: { + icon: MenuItemProps['icon']; + text: string; + }; +}; +export const $customStarUI = atom(undefined); diff --git a/invokeai/frontend/web/src/app/store/nanostores/headerComponent.ts b/invokeai/frontend/web/src/app/store/nanostores/headerComponent.ts new file mode 100644 index 0000000000..90a4775ff9 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/nanostores/headerComponent.ts @@ -0,0 +1,4 @@ +import { atom } from 'nanostores'; +import { ReactNode } from 'react'; + +export const $headerComponent = atom(undefined); diff --git a/invokeai/frontend/web/src/app/store/nanostores/index.ts b/invokeai/frontend/web/src/app/store/nanostores/index.ts new file mode 100644 index 0000000000..ae43ed3035 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/nanostores/index.ts @@ -0,0 +1,3 @@ +/** + * For non-serializable data that needs to be available throughout the app, or when redux is not appropriate, use nanostores. + */ diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index ce2a21c6e7..29caa69cbe 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -86,10 +86,7 @@ export const store = configureStore({ .concat(autoBatchEnhancer()); }, middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ - immutableCheck: false, - serializableCheck: false, - }) + getDefaultMiddleware() .concat(api.middleware) .concat(dynamicMiddlewares) .prepend(listenerMiddleware.middleware), diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx index e2a9013aac..29b45761ee 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx @@ -1,4 +1,6 @@ import { MenuItem } from '@chakra-ui/react'; +import { useStore } from '@nanostores/react'; +import { $customStarUI } from 'app/store/nanostores/customStarUI'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { imagesToChangeSelected, @@ -12,12 +14,11 @@ import { useStarImagesMutation, useUnstarImagesMutation, } from '../../../../services/api/endpoints/images'; -import { uiSelector } from '../../../ui/store/uiSelectors'; const MultipleSelectionMenuItems = () => { const dispatch = useAppDispatch(); const selection = useAppSelector((state) => state.gallery.selection); - const { customStarUi } = useAppSelector(uiSelector); + const customStarUi = useStore($customStarUI); const [starImages] = useStarImagesMutation(); const [unstarImages] = useUnstarImagesMutation(); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx index 68c5c2e1ae..e5b9d94578 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx @@ -1,5 +1,7 @@ import { Flex, MenuItem, Spinner } from '@chakra-ui/react'; +import { useStore } from '@nanostores/react'; import { useAppToaster } from 'app/components/Toaster'; +import { $customStarUI } from 'app/store/nanostores/customStarUI'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { @@ -7,6 +9,7 @@ import { isModalOpenChanged, } from 'features/changeBoardModal/store/slice'; import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice'; +import { workflowLoadRequested } from 'features/nodes/store/actions'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { initialImageSelected } from 'features/parameters/store/actions'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; @@ -32,10 +35,8 @@ import { useUnstarImagesMutation, } from 'services/api/endpoints/images'; import { ImageDTO } from 'services/api/types'; -import { sentImageToCanvas, sentImageToImg2Img } from '../../store/actions'; -import { workflowLoadRequested } from 'features/nodes/store/actions'; import { configSelector } from '../../../system/store/configSelectors'; -import { uiSelector } from '../../../ui/store/uiSelectors'; +import { sentImageToCanvas, sentImageToImg2Img } from '../../store/actions'; type SingleSelectionMenuItemsProps = { imageDTO: ImageDTO; @@ -51,7 +52,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => { const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled; const { shouldFetchMetadataFromApi } = useAppSelector(configSelector); - const { customStarUi } = useAppSelector(uiSelector); + const customStarUi = useStore($customStarUI); const { metadata, workflow, isLoading } = useGetImageMetadataFromFileQuery( { image: imageDTO, shouldFetchMetadataFromApi }, diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx index 6c34bc31b8..af01eeaea8 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx @@ -1,4 +1,6 @@ import { Box, Flex } from '@chakra-ui/react'; +import { useStore } from '@nanostores/react'; +import { $customStarUI } from 'app/store/nanostores/customStarUI'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIDndImage from 'common/components/IAIDndImage'; import IAIFillSkeleton from 'common/components/IAIFillSkeleton'; @@ -10,6 +12,7 @@ import { } from 'features/dnd/types'; import { useMultiselect } from 'features/gallery/hooks/useMultiselect'; import { MouseEvent, memo, useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { FaTrash } from 'react-icons/fa'; import { MdStar, MdStarBorder } from 'react-icons/md'; import { @@ -18,8 +21,6 @@ import { useUnstarImagesMutation, } from 'services/api/endpoints/images'; import IAIDndImageIcon from '../../../../common/components/IAIDndImageIcon'; -import { uiSelector } from '../../../ui/store/uiSelectors'; -import { useTranslation } from 'react-i18next'; interface HoverableImageProps { imageName: string; @@ -35,7 +36,7 @@ const GalleryImage = (props: HoverableImageProps) => { const { handleClick, isSelected, selection, selectionCount } = useMultiselect(imageDTO); - const { customStarUi } = useAppSelector(uiSelector); + const customStarUi = useStore($customStarUI); const handleDelete = useCallback( (e: MouseEvent) => { diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 33edc6308f..82c9ef4e77 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -4,7 +4,7 @@ import { initialImageChanged } from 'features/parameters/store/generationSlice'; import { SchedulerParam } from 'features/parameters/types/parameterSchemas'; import { setActiveTabReducer } from './extraReducers'; import { InvokeTabName } from './tabMap'; -import { CustomStarUi, UIState } from './uiTypes'; +import { UIState } from './uiTypes'; export const initialUIState: UIState = { activeTab: 0, @@ -19,7 +19,6 @@ export const initialUIState: UIState = { favoriteSchedulers: [], globalContextMenuCloseTrigger: 0, panels: {}, - customStarUi: undefined, }; export const uiSlice = createSlice({ @@ -71,9 +70,6 @@ export const uiSlice = createSlice({ ) => { state.panels[action.payload.name] = action.payload.value; }, - setCustomStarUi: (state, action: PayloadAction) => { - state.customStarUi = action.payload; - }, }, extraReducers(builder) { builder.addCase(initialImageChanged, (state) => { @@ -95,7 +91,6 @@ export const { setShouldAutoChangeDimensions, contextMenusClosed, panelsChanged, - setCustomStarUi, } = uiSlice.actions; export default uiSlice.reducer; diff --git a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts index 8f7b1999a4..41a359a651 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts @@ -1,4 +1,3 @@ -import { MenuItemProps } from '@chakra-ui/react'; import { SchedulerParam } from 'features/parameters/types/parameterSchemas'; export type Coordinates = { @@ -13,17 +12,6 @@ export type Dimensions = { export type Rect = Coordinates & Dimensions; -export type CustomStarUi = { - on: { - icon: MenuItemProps['icon']; - text: string; - }; - off: { - icon: MenuItemProps['icon']; - text: string; - }; -}; - export interface UIState { activeTab: number; shouldShowImageDetails: boolean; @@ -37,5 +25,4 @@ export interface UIState { favoriteSchedulers: SchedulerParam[]; globalContextMenuCloseTrigger: number; panels: Record; - customStarUi?: CustomStarUi; }