From 34c563060fffbd01eb9d96de0cc187998817c528 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Wed, 27 Sep 2023 00:06:39 +1000 Subject: [PATCH] feat(ui): store active tab as name, not index (#4697) This fixes an issue with tab changing when some tabs are disabled. --- .../src/features/ui/components/InvokeTabs.tsx | 16 +++++----- .../src/features/ui/store/extraReducers.ts | 13 -------- .../web/src/features/ui/store/uiSelectors.ts | 30 ++++++++----------- .../web/src/features/ui/store/uiSlice.ts | 7 ++--- .../web/src/features/ui/store/uiTypes.ts | 3 +- 5 files changed, 26 insertions(+), 43 deletions(-) delete mode 100644 invokeai/frontend/web/src/features/ui/store/extraReducers.ts diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index fb5756b121..ac7b8aa1c4 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -14,7 +14,7 @@ import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent'; import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditorPanelGroup'; -import { InvokeTabName, tabMap } from 'features/ui/store/tabMap'; +import { InvokeTabName } from 'features/ui/store/tabMap'; import { setActiveTab } from 'features/ui/store/uiSlice'; import { ResourceKey } from 'i18next'; import { isEqual } from 'lodash-es'; @@ -110,7 +110,7 @@ export const NO_GALLERY_TABS: InvokeTabName[] = ['modelManager', 'queue']; export const NO_SIDE_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue']; const InvokeTabs = () => { - const activeTab = useAppSelector(activeTabIndexSelector); + const activeTabIndex = useAppSelector(activeTabIndexSelector); const activeTabName = useAppSelector(activeTabNameSelector); const enabledTabs = useAppSelector(enabledTabsSelector); const { t } = useTranslation(); @@ -150,13 +150,13 @@ const InvokeTabs = () => { const handleTabChange = useCallback( (index: number) => { - const activeTabName = tabMap[index]; - if (!activeTabName) { + const tab = enabledTabs[index]; + if (!tab) { return; } - dispatch(setActiveTab(activeTabName)); + dispatch(setActiveTab(tab.id)); }, - [dispatch] + [dispatch, enabledTabs] ); const { @@ -216,8 +216,8 @@ const InvokeTabs = () => { return ( { - if (typeof newActiveTab === 'number') { - state.activeTab = newActiveTab; - } else { - state.activeTab = tabMap.indexOf(newActiveTab); - } -}; diff --git a/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts b/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts index 5427fa9d3b..99ee8d80f7 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSelectors.ts @@ -1,27 +1,23 @@ import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; -import { isEqual } from 'lodash-es'; - -import { InvokeTabName, tabMap } from './tabMap'; -import { UIState } from './uiTypes'; +import { isEqual, isString } from 'lodash-es'; +import { tabMap } from './tabMap'; export const activeTabNameSelector = createSelector( - (state: RootState) => state.ui, - (ui: UIState) => tabMap[ui.activeTab] as InvokeTabName, - { - memoizeOptions: { - equalityCheck: isEqual, - }, - } + (state: RootState) => state, + /** + * Previously `activeTab` was an integer, but now it's a string. + * Default to first tab in case user has integer. + */ + ({ ui }) => (isString(ui.activeTab) ? ui.activeTab : 'txt2img') ); export const activeTabIndexSelector = createSelector( - (state: RootState) => state.ui, - (ui: UIState) => ui.activeTab, - { - memoizeOptions: { - equalityCheck: isEqual, - }, + (state: RootState) => state, + ({ ui, config }) => { + const tabs = tabMap.filter((t) => !config.disabledTabs.includes(t)); + const idx = tabs.indexOf(ui.activeTab); + return idx === -1 ? 0 : idx; } ); diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index 82c9ef4e77..9782d0bfac 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -2,12 +2,11 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; 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'; export const initialUIState: UIState = { - activeTab: 0, + activeTab: 'txt2img', shouldShowImageDetails: false, shouldUseCanvasBetaLayout: false, shouldShowExistingModelsInSearch: false, @@ -26,7 +25,7 @@ export const uiSlice = createSlice({ initialState: initialUIState, reducers: { setActiveTab: (state, action: PayloadAction) => { - setActiveTabReducer(state, action.payload); + state.activeTab = action.payload; }, setShouldShowImageDetails: (state, action: PayloadAction) => { state.shouldShowImageDetails = action.payload; @@ -73,7 +72,7 @@ export const uiSlice = createSlice({ }, extraReducers(builder) { builder.addCase(initialImageChanged, (state) => { - setActiveTabReducer(state, 'img2img'); + state.activeTab = 'img2img'; }); }, }); diff --git a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts index 41a359a651..1b9fee6989 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiTypes.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiTypes.ts @@ -1,4 +1,5 @@ import { SchedulerParam } from 'features/parameters/types/parameterSchemas'; +import { InvokeTabName } from './tabMap'; export type Coordinates = { x: number; @@ -13,7 +14,7 @@ export type Dimensions = { export type Rect = Coordinates & Dimensions; export interface UIState { - activeTab: number; + activeTab: InvokeTabName; shouldShowImageDetails: boolean; shouldUseCanvasBetaLayout: boolean; shouldShowExistingModelsInSearch: boolean;