feat(ui): move layers/gallery tab state into redux so it persists across sessions/refreshes, make gallery the default

This commit is contained in:
Mary Hipp
2024-10-15 10:10:40 -04:00
committed by psychedelicious
parent ad1a8fbb8d
commit a00347379b
11 changed files with 64 additions and 57 deletions

View File

@@ -2,14 +2,10 @@ import { Alert, AlertDescription, AlertIcon, AlertTitle, Button, Flex } from '@i
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useBoolean } from 'common/hooks/useBoolean';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import {
selectCanvasRightPanelGalleryTab,
selectCanvasRightPanelLayersTab,
} from 'features/controlLayers/store/ephemeral';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useCurrentDestination } from 'features/queue/hooks/useCurrentDestination';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { activeTabCanvasRightPanelChanged, setActiveTab } from 'features/ui/store/uiSlice';
import { AnimatePresence, motion } from 'framer-motion';
import type { PropsWithChildren, ReactNode } from 'react';
import { useCallback, useMemo } from 'react';
@@ -17,10 +13,11 @@ import { Trans, useTranslation } from 'react-i18next';
const ActivateImageViewerButton = (props: PropsWithChildren) => {
const imageViewer = useImageViewer();
const dispatch = useAppDispatch();
const onClick = useCallback(() => {
imageViewer.open();
selectCanvasRightPanelGalleryTab();
}, [imageViewer]);
dispatch(activeTabCanvasRightPanelChanged('gallery'));
}, [imageViewer, dispatch]);
return (
<Button onClick={onClick} size="sm" variant="link" color="base.50">
{props.children}
@@ -60,7 +57,7 @@ const ActivateCanvasButton = (props: PropsWithChildren) => {
const imageViewer = useImageViewer();
const onClick = useCallback(() => {
dispatch(setActiveTab('canvas'));
selectCanvasRightPanelLayersTab();
dispatch(activeTabCanvasRightPanelChanged('layers'));
imageViewer.close();
}, [dispatch, imageViewer]);
return (

View File

@@ -1,31 +1,49 @@
import { useDndContext } from '@dnd-kit/core';
import { Box, Button, Spacer, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { CanvasLayersPanelContent } from 'features/controlLayers/components/CanvasLayersPanelContent';
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import {
$canvasRightPanelTabIndex,
selectCanvasRightPanelGalleryTab,
selectCanvasRightPanelLayersTab,
} from 'features/controlLayers/store/ephemeral';
import { selectEntityCountActive } from 'features/controlLayers/store/selectors';
import GalleryPanelContent from 'features/gallery/components/GalleryPanelContent';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { selectActiveTabCanvasRightPanel } from 'features/ui/store/uiSelectors';
import { activeTabCanvasRightPanelChanged } from 'features/ui/store/uiSlice';
import { memo, useCallback, useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
export const CanvasRightPanel = memo(() => {
const { t } = useTranslation();
const tabIndex = useStore($canvasRightPanelTabIndex);
const activeTab = useAppSelector(selectActiveTabCanvasRightPanel);
const imageViewer = useImageViewer();
const dispatch = useAppDispatch();
const tabIndex = useMemo(() => {
if (activeTab === 'gallery') {
return 1;
} else {
return 0;
}
}, [activeTab]);
const onClickViewerToggleButton = useCallback(() => {
if ($canvasRightPanelTabIndex.get() !== 1) {
$canvasRightPanelTabIndex.set(1);
if (activeTab !== 'gallery') {
dispatch(activeTabCanvasRightPanelChanged('gallery'));
}
imageViewer.toggle();
}, [imageViewer]);
}, [imageViewer, activeTab, dispatch]);
const onChangeTab = useCallback(
(index: number) => {
if (index === 0) {
dispatch(activeTabCanvasRightPanelChanged('layers'));
} else {
dispatch(activeTabCanvasRightPanelChanged('gallery'));
}
},
[dispatch]
);
useRegisteredHotkeys({
id: 'toggleViewer',
category: 'viewer',
@@ -34,7 +52,7 @@ export const CanvasRightPanel = memo(() => {
});
return (
<Tabs index={tabIndex} onChange={$canvasRightPanelTabIndex.set} w="full" h="full" display="flex" flexDir="column">
<Tabs index={tabIndex} onChange={onChangeTab} w="full" h="full" display="flex" flexDir="column">
<TabList alignItems="center">
<PanelTabs />
<Spacer />
@@ -63,22 +81,23 @@ const PanelTabs = memo(() => {
const activeEntityCount = useAppSelector(selectEntityCountActive);
const tabTimeout = useRef<number | null>(null);
const dndCtx = useDndContext();
const dispatch = useAppDispatch();
const onOnMouseOverLayersTab = useCallback(() => {
tabTimeout.current = window.setTimeout(() => {
if (dndCtx.active) {
selectCanvasRightPanelLayersTab();
dispatch(activeTabCanvasRightPanelChanged('layers'));
}
}, 300);
}, [dndCtx.active]);
}, [dndCtx.active, dispatch]);
const onOnMouseOverGalleryTab = useCallback(() => {
tabTimeout.current = window.setTimeout(() => {
if (dndCtx.active) {
selectCanvasRightPanelGalleryTab();
dispatch(activeTabCanvasRightPanelChanged('gallery'));
}
}, 300);
}, [dndCtx.active]);
}, [dndCtx.active, dispatch]);
const onMouseOut = useCallback(() => {
if (tabTimeout.current) {

View File

@@ -3,15 +3,12 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { buildUseBoolean } from 'common/hooks/useBoolean';
import { newCanvasSessionRequested, newGallerySessionRequested } from 'features/controlLayers/store/actions';
import {
selectCanvasRightPanelGalleryTab,
selectCanvasRightPanelLayersTab,
} from 'features/controlLayers/store/ephemeral';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import {
selectSystemShouldConfirmOnNewSession,
shouldConfirmOnNewSessionToggled,
} from 'features/system/store/systemSlice';
import { activeTabCanvasRightPanelChanged } from 'features/ui/store/uiSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -27,7 +24,7 @@ export const useNewGallerySession = () => {
const newGallerySessionImmediate = useCallback(() => {
dispatch(newGallerySessionRequested());
imageViewer.open();
selectCanvasRightPanelGalleryTab();
dispatch(activeTabCanvasRightPanelChanged('gallery'));
}, [dispatch, imageViewer]);
const newGallerySessionWithDialog = useCallback(() => {
@@ -50,7 +47,7 @@ export const useNewCanvasSession = () => {
const newCanvasSessionImmediate = useCallback(() => {
dispatch(newCanvasSessionRequested());
imageViewer.close();
selectCanvasRightPanelLayersTab();
dispatch(activeTabCanvasRightPanelChanged('layers'));
}, [dispatch, imageViewer]);
const newCanvasSessionWithDialog = useCallback(() => {

View File

@@ -1,13 +1,11 @@
import { useStore } from '@nanostores/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
import { entityDeleted } from 'features/controlLayers/store/canvasSlice';
import { $canvasRightPanelTab } from 'features/controlLayers/store/ephemeral';
import { selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { selectActiveTab, selectActiveTabCanvasRightPanel } from 'features/ui/store/uiSelectors';
import { useCallback, useMemo } from 'react';
export function useCanvasDeleteLayerHotkey() {
@@ -15,7 +13,7 @@ export function useCanvasDeleteLayerHotkey() {
const dispatch = useAppDispatch();
const selectedEntityIdentifier = useAppSelector(selectSelectedEntityIdentifier);
const isBusy = useCanvasIsBusy();
const canvasRightPanelTab = useStore($canvasRightPanelTab);
const canvasRightPanelTab = useAppSelector(selectActiveTabCanvasRightPanel);
const appTab = useAppSelector(selectActiveTab);
const imageViewer = useImageViewer();

View File

@@ -1,5 +1,5 @@
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
import { atom, computed } from 'nanostores';
import { atom } from 'nanostores';
// Ephemeral state for canvas - not persisted across sessions.
@@ -7,16 +7,3 @@ import { atom, computed } from 'nanostores';
* The global canvas manager instance.
*/
export const $canvasManager = atom<CanvasManager | null>(null);
/**
* The index of the active tab in the canvas right panel.
*/
export const $canvasRightPanelTabIndex = atom(0);
/**
* The name of the active tab in the canvas right panel.
*/
export const $canvasRightPanelTab = computed($canvasRightPanelTabIndex, (index) =>
index === 0 ? 'layers' : 'gallery'
);
export const selectCanvasRightPanelLayersTab = () => $canvasRightPanelTabIndex.set(0);
export const selectCanvasRightPanelGalleryTab = () => $canvasRightPanelTabIndex.set(1);