From d989c7fa34d0f4981862e0decef62577fe978a6e Mon Sep 17 00:00:00 2001 From: Mary Hipp Rogers Date: Wed, 13 Sep 2023 16:48:10 -0400 Subject: [PATCH] add option for custom star ui (#4530) Co-authored-by: Mary Hipp --- .../frontend/web/src/app/components/App.tsx | 10 +++++++++ .../web/src/app/components/InvokeAIUI.tsx | 4 ++++ .../MultipleSelectionMenuItems.tsx | 13 ++++++++---- .../SingleSelectionMenuItems.tsx | 16 ++++++++++---- .../components/ImageGrid/GalleryImage.tsx | 21 +++++++++++++++---- .../web/src/features/ui/store/uiSlice.ts | 7 ++++++- .../web/src/features/ui/store/uiTypes.ts | 13 ++++++++++++ 7 files changed, 71 insertions(+), 13 deletions(-) diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index a70ed03fda..aa1919abfb 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -18,6 +18,8 @@ import { usePreselectedImage } from '../../features/parameters/hooks/usePreselec 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'; const DEFAULT_CONFIG = {}; @@ -28,12 +30,14 @@ interface Props { imageName: string; action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters'; }; + customStarUi?: CustomStarUi; } const App = ({ config = DEFAULT_CONFIG, headerComponent, selectedImage, + customStarUi, }: Props) => { const language = useAppSelector(languageSelector); @@ -57,6 +61,12 @@ const App = ({ } }, [dispatch, config, logger]); + useEffect(() => { + if (customStarUi) { + dispatch(setCustomStarUi(customStarUi)); + } + }, [customStarUi, dispatch]); + useEffect(() => { dispatch(appStarted()); }, [dispatch]); diff --git a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx index 322ee3fd2b..36636b3ca9 100644 --- a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx +++ b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx @@ -15,6 +15,7 @@ import { socketMiddleware } from 'services/events/middleware'; import Loading from '../../common/components/Loading/Loading'; import '../../i18n'; import AppDndContext from '../../features/dnd/components/AppDndContext'; +import { CustomStarUi } from '../../features/ui/store/uiTypes'; const App = lazy(() => import('./App')); const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider')); @@ -30,6 +31,7 @@ interface Props extends PropsWithChildren { imageName: string; action: 'sendToImg2Img' | 'sendToCanvas' | 'useAllParameters'; }; + customStarUi?: CustomStarUi; } const InvokeAIUI = ({ @@ -40,6 +42,7 @@ const InvokeAIUI = ({ middleware, projectId, selectedImage, + customStarUi, }: Props) => { useEffect(() => { // configure API client token @@ -90,6 +93,7 @@ const InvokeAIUI = ({ config={config} headerComponent={headerComponent} selectedImage={selectedImage} + customStarUi={customStarUi} /> 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 bf2b344b4c..e2a9013aac 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/MultipleSelectionMenuItems.tsx @@ -12,10 +12,12 @@ 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 [starImages] = useStarImagesMutation(); const [unstarImages] = useUnstarImagesMutation(); @@ -49,15 +51,18 @@ const MultipleSelectionMenuItems = () => { <> {areAllStarred && ( } + icon={customStarUi ? customStarUi.on.icon : } onClickCapture={handleUnstarSelection} > - Unstar All + {customStarUi ? customStarUi.off.text : `Unstar All`} )} {(areAllUnstarred || (!areAllStarred && !areAllUnstarred)) && ( - } onClickCapture={handleStarSelection}> - Star All + } + onClickCapture={handleStarSelection} + > + {customStarUi ? customStarUi.on.text : `Star All`} )} } onClickCapture={handleChangeBoard}> 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 46c84e85ce..68c5c2e1ae 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx @@ -35,6 +35,7 @@ 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'; type SingleSelectionMenuItemsProps = { imageDTO: ImageDTO; @@ -50,6 +51,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => { const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled; const { shouldFetchMetadataFromApi } = useAppSelector(configSelector); + const { customStarUi } = useAppSelector(uiSelector); const { metadata, workflow, isLoading } = useGetImageMetadataFromFileQuery( { image: imageDTO, shouldFetchMetadataFromApi }, @@ -225,12 +227,18 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => { Change Board {imageDTO.starred ? ( - } onClickCapture={handleUnstarImage}> - Unstar Image + } + onClickCapture={handleUnstarImage} + > + {customStarUi ? customStarUi.off.text : `Unstar Image`} ) : ( - } onClickCapture={handleStarImage}> - Star Image + } + onClickCapture={handleStarImage} + > + {customStarUi ? customStarUi.on.text : `Star Image`} )} { const { handleClick, isSelected, selection, selectionCount } = useMultiselect(imageDTO); + const { customStarUi } = useAppSelector(uiSelector); + const handleDelete = useCallback( (e: MouseEvent) => { e.stopPropagation(); @@ -91,12 +94,22 @@ const GalleryImage = (props: HoverableImageProps) => { const starIcon = useMemo(() => { if (imageDTO?.starred) { - return ; + return customStarUi ? customStarUi.on.icon : ; } if (!imageDTO?.starred && isHovered) { - return ; + return customStarUi ? customStarUi.off.icon : ; } - }, [imageDTO?.starred, isHovered]); + }, [imageDTO?.starred, isHovered, customStarUi]); + + const starTooltip = useMemo(() => { + if (imageDTO?.starred) { + return customStarUi ? customStarUi.off.text : 'Unstar'; + } + if (!imageDTO?.starred) { + return customStarUi ? customStarUi.on.text : 'Star'; + } + return ''; + }, [imageDTO?.starred, customStarUi]); if (!imageDTO) { return ; @@ -131,7 +144,7 @@ const GalleryImage = (props: HoverableImageProps) => { {isHovered && shift && ( diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 82c9ef4e77..33edc6308f 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 { UIState } from './uiTypes'; +import { CustomStarUi, UIState } from './uiTypes'; export const initialUIState: UIState = { activeTab: 0, @@ -19,6 +19,7 @@ export const initialUIState: UIState = { favoriteSchedulers: [], globalContextMenuCloseTrigger: 0, panels: {}, + customStarUi: undefined, }; export const uiSlice = createSlice({ @@ -70,6 +71,9 @@ 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) => { @@ -91,6 +95,7 @@ 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 41a359a651..8f7b1999a4 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts @@ -1,3 +1,4 @@ +import { MenuItemProps } from '@chakra-ui/react'; import { SchedulerParam } from 'features/parameters/types/parameterSchemas'; export type Coordinates = { @@ -12,6 +13,17 @@ 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; @@ -25,4 +37,5 @@ export interface UIState { favoriteSchedulers: SchedulerParam[]; globalContextMenuCloseTrigger: number; panels: Record; + customStarUi?: CustomStarUi; }