feat(ui): reworked layout (wip)

This commit is contained in:
psychedelicious
2024-09-09 21:53:36 +10:00
parent b67c369bdb
commit 3ed29a16a8
52 changed files with 656 additions and 628 deletions

View File

@@ -14,12 +14,14 @@ import {
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useGallerySearchTerm } from 'features/gallery/components/ImageGrid/useGallerySearchTerm';
import { selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import { galleryViewChanged, selectGallerySlice } from 'features/gallery/store/gallerySlice';
import CurrentImageButtons from 'features/gallery/components/ImageViewer/CurrentImageButtons';
import CurrentImagePreview from 'features/gallery/components/ImageViewer/CurrentImagePreview';
import { selectIsMiniViewerOpen, selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import { galleryViewChanged, isMiniViewerOpenToggled, selectGallerySlice } from 'features/gallery/store/gallerySlice';
import type { CSSProperties } from 'react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiMagnifyingGlassBold } from 'react-icons/pi';
import { PiEyeBold, PiEyeClosedBold, PiMagnifyingGlassBold } from 'react-icons/pi';
import { useBoardName } from 'services/api/hooks/useBoardName';
import GalleryImageGrid from './ImageGrid/GalleryImageGrid';
@@ -38,7 +40,7 @@ const SELECTED_STYLES: ChakraProps['sx'] = {
color: 'invokeBlue.300',
};
const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0 };
const COLLAPSE_STYLES: CSSProperties = { flexShrink: 0, minHeight: 0, width: '100%' };
const selectGalleryView = createSelector(selectGallerySlice, (gallery) => gallery.galleryView);
const selectSearchTerm = createSelector(selectGallerySlice, (gallery) => gallery.searchTerm);
@@ -50,7 +52,11 @@ export const Gallery = () => {
const initialSearchTerm = useAppSelector(selectSearchTerm);
const searchDisclosure = useDisclosure({ defaultIsOpen: initialSearchTerm.length > 0 });
const [searchTerm, onChangeSearchTerm, onResetSearchTerm] = useGallerySearchTerm();
const isMiniViewerOpen = useAppSelector(selectIsMiniViewerOpen);
const toggleMiniViewer = useCallback(() => {
dispatch(isMiniViewerOpenToggled());
}, [dispatch]);
const handleClickImages = useCallback(() => {
dispatch(galleryViewChanged('images'));
}, [dispatch]);
@@ -68,7 +74,7 @@ export const Gallery = () => {
const boardName = useBoardName(selectedBoardId);
return (
<Flex flexDirection="column" alignItems="center" justifyContent="space-between" h="full" w="full" pt={1}>
<Flex flexDirection="column" alignItems="center" justifyContent="space-between" h="full" w="full" pt={1} minH={0}>
<Tabs index={galleryView === 'images' ? 0 : 1} variant="enclosed" display="flex" flexDir="column" w="full">
<TabList gap={2} fontSize="sm" borderColor="base.800" alignItems="center" w="full">
<Text fontSize="sm" fontWeight="semibold" noOfLines={1} px="2" wordBreak="break-all">
@@ -81,28 +87,54 @@ export const Gallery = () => {
<Tab sx={BASE_STYLES} _selected={SELECTED_STYLES} onClick={handleClickAssets} data-testid="assets-tab">
{t('gallery.assets')}
</Tab>
<IconButton
onClick={handleClickSearch}
tooltip={searchDisclosure.isOpen ? `${t('gallery.exitSearch')}` : `${t('gallery.displaySearch')}`}
aria-label={t('gallery.displaySearch')}
icon={<PiMagnifyingGlassBold />}
colorScheme={searchDisclosure.isOpen ? 'invokeBlue' : 'base'}
variant="link"
/>
<Flex h="full">
<IconButton
size="sm"
variant="link"
alignSelf="stretch"
onClick={toggleMiniViewer}
tooltip={t('gallery.toggleMiniViewer')}
aria-label={t('gallery.toggleMiniViewer')}
icon={isMiniViewerOpen ? <PiEyeBold /> : <PiEyeClosedBold />}
colorScheme={isMiniViewerOpen ? 'invokeBlue' : 'base'}
/>
<IconButton
size="sm"
variant="link"
alignSelf="stretch"
onClick={handleClickSearch}
tooltip={searchDisclosure.isOpen ? `${t('gallery.exitSearch')}` : `${t('gallery.displaySearch')}`}
aria-label={t('gallery.displaySearch')}
icon={<PiMagnifyingGlassBold />}
/>
</Flex>
</TabList>
</Tabs>
<Box w="full">
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
<Box w="full" pt={2}>
<GallerySearch
searchTerm={searchTerm}
onChangeSearchTerm={onChangeSearchTerm}
onResetSearchTerm={onResetSearchTerm}
/>
</Box>
</Collapse>
</Box>
<Collapse in={searchDisclosure.isOpen} style={COLLAPSE_STYLES}>
<Box w="full" pt={2}>
<GallerySearch
searchTerm={searchTerm}
onChangeSearchTerm={onChangeSearchTerm}
onResetSearchTerm={onResetSearchTerm}
/>
</Box>
</Collapse>
<Collapse in={isMiniViewerOpen} style={COLLAPSE_STYLES}>
<Box position="relative" w="full" mt={2} aspectRatio="1/1">
<CurrentImagePreview />
<Flex
position="absolute"
top={2}
gap={2}
justifyContent="space-between"
left="50%"
transform="translateX(-50%)"
>
<CurrentImageButtons />
</Flex>
</Box>
</Collapse>
<GalleryImageGrid />
<GalleryPagination />
</Flex>

View File

@@ -1,4 +1,4 @@
import { Box, Button, Collapse, Divider, Flex, IconButton, useDisclosure } from '@invoke-ai/ui-library';
import { Box, Button, Collapse, Divider, Flex, IconButton, Spacer, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useScopeOnFocus } from 'common/hooks/interactionScopes';
import { GalleryHeader } from 'features/gallery/components/GalleryHeader';
@@ -60,36 +60,31 @@ const GalleryPanelContent = () => {
return (
<Flex ref={ref} position="relative" flexDirection="column" h="full" w="full" tabIndex={-1}>
<Flex alignItems="center" gap={0}>
<GalleryHeader />
<Flex alignItems="center" justifyContent="space-between" w="full">
<Button
<GalleryHeader />
<Flex alignItems="center" w="full">
<Button
size="sm"
variant="ghost"
onClick={handleToggleBoardPanel}
rightIcon={boardsListPanel.isCollapsed ? <PiCaretDownBold /> : <PiCaretUpBold />}
>
{boardsListPanel.isCollapsed ? t('boards.viewBoards') : t('boards.hideBoards')}
</Button>
<Spacer />
<Flex h="full">
<GallerySettingsPopover />
<IconButton
size="sm"
variant="ghost"
onClick={handleToggleBoardPanel}
rightIcon={boardsListPanel.isCollapsed ? <PiCaretDownBold /> : <PiCaretUpBold />}
>
{boardsListPanel.isCollapsed ? t('boards.viewBoards') : t('boards.hideBoards')}
</Button>
<Flex alignItems="center" justifyContent="space-between">
<GallerySettingsPopover />
<Flex>
<IconButton
w="full"
h="full"
onClick={handleClickBoardSearch}
tooltip={
boardSearchDisclosure.isOpen
? `${t('gallery.exitBoardSearch')}`
: `${t('gallery.displayBoardSearch')}`
}
aria-label={t('gallery.displayBoardSearch')}
icon={<PiMagnifyingGlassBold />}
colorScheme={boardSearchDisclosure.isOpen ? 'invokeBlue' : 'base'}
variant="link"
/>
</Flex>
</Flex>
variant="link"
alignSelf="stretch"
onClick={handleClickBoardSearch}
tooltip={
boardSearchDisclosure.isOpen ? `${t('gallery.exitBoardSearch')}` : `${t('gallery.displayBoardSearch')}`
}
aria-label={t('gallery.displayBoardSearch')}
icon={<PiMagnifyingGlassBold />}
colorScheme={boardSearchDisclosure.isOpen ? 'invokeBlue' : 'base'}
/>
</Flex>
</Flex>

View File

@@ -17,7 +17,13 @@ const GallerySettingsPopover = () => {
return (
<Popover isLazy>
<PopoverTrigger>
<IconButton aria-label={t('gallery.gallerySettings')} icon={<RiSettings4Fill />} variant="link" h="full" />
<IconButton
size="sm"
variant="link"
alignSelf="stretch"
aria-label={t('gallery.gallerySettings')}
icon={<RiSettings4Fill />}
/>
</PopoverTrigger>
<PopoverContent>
<PopoverBody>

View File

@@ -1,8 +1,8 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { imageSelected, imageToCompareChanged } from 'features/gallery/store/gallerySlice';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiEyeBold } from 'react-icons/pi';
@@ -11,13 +11,12 @@ export const ImageMenuItemOpenInViewer = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const imageDTO = useImageDTOContext();
const imageViewer = useImageViewer();
const onClick = useCallback(() => {
dispatch(imageToCompareChanged(null));
dispatch(imageSelected(imageDTO));
imageViewer.onOpen();
}, [dispatch, imageDTO, imageViewer]);
dispatch(setActiveTab('gallery'));
}, [dispatch, imageDTO]);
return (
<MenuItem icon={<PiEyeBold />} onClick={onClick}>

View File

@@ -5,7 +5,6 @@ import { rasterLayerAdded } from 'features/controlLayers/store/canvasSlice';
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
import type { CanvasRasterLayerState } from 'features/controlLayers/store/types';
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { sentImageToCanvas } from 'features/gallery/store/actions';
import { toast } from 'features/toast/toast';
@@ -21,7 +20,6 @@ export const ImageMenuItemSendToCanvas = memo(() => {
const dispatch = useAppDispatch();
const imageDTO = useImageDTOContext();
const bboxRect = useAppSelector(selectBboxRect);
const imageViewer = useImageViewer();
const handleSendToCanvas = useCallback(() => {
const imageObject = imageDTOToImageObject(imageDTO);
@@ -32,13 +30,12 @@ export const ImageMenuItemSendToCanvas = memo(() => {
dispatch(sentImageToCanvas());
dispatch(rasterLayerAdded({ overrides, isSelected: true }));
dispatch(setActiveTab('generation'));
imageViewer.onClose();
toast({
id: 'SENT_TO_CANVAS',
title: t('toast.sentToCanvas'),
status: 'success',
});
}, [bboxRect.x, bboxRect.y, dispatch, imageDTO, imageViewer, t]);
}, [bboxRect.x, bboxRect.y, dispatch, imageDTO, t]);
return (
<MenuItem icon={<PiShareFatBold />} onClickCapture={handleSendToCanvas} id="send-to-canvas">

View File

@@ -13,11 +13,8 @@ import { getGalleryImageDataTestId } from 'features/gallery/components/ImageGrid
import { useMultiselect } from 'features/gallery/hooks/useMultiselect';
import { useScrollIntoView } from 'features/gallery/hooks/useScrollIntoView';
import { selectSelectedBoardId } from 'features/gallery/store/gallerySelectors';
import {
imageToCompareChanged,
isImageViewerOpenChanged,
selectGallerySlice,
} from 'features/gallery/store/gallerySlice';
import { imageToCompareChanged, selectGallerySlice } from 'features/gallery/store/gallerySlice';
import { setActiveTab } from 'features/ui/store/uiSlice';
import type { MouseEvent, MouseEventHandler } from 'react';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -116,7 +113,7 @@ const GalleryImage = ({ index, imageDTO }: HoverableImageProps) => {
}, []);
const onDoubleClick = useCallback(() => {
dispatch(isImageViewerOpenChanged(true));
dispatch(setActiveTab('gallery'));
dispatch(imageToCompareChanged(null));
}, [dispatch]);
@@ -150,7 +147,7 @@ const GalleryImage = ({ index, imageDTO }: HoverableImageProps) => {
}
return (
<Box w="full" h="full" p={1.5} className={GALLERY_IMAGE_CLASS_NAME} data-testid={dataTestId} sx={boxSx}>
<Box w="full" h="full" className={GALLERY_IMAGE_CLASS_NAME} data-testid={dataTestId} sx={boxSx}>
<Flex
ref={imageContainerRef}
userSelect="none"
@@ -183,13 +180,12 @@ const GalleryImage = ({ index, imageDTO }: HoverableImageProps) => {
color="base.50"
fontSize="sm"
fontWeight="semibold"
bottom={0}
left={0}
bottom={1}
left={1}
opacity={0.7}
px={2}
lineHeight={1.25}
borderTopEndRadius="base"
borderBottomStartRadius="base"
sx={badgeSx}
pointerEvents="none"
>{`${imageDTO.width}x${imageDTO.height}`}</Text>
@@ -199,8 +195,8 @@ const GalleryImage = ({ index, imageDTO }: HoverableImageProps) => {
icon={starIcon}
tooltip={starTooltip}
position="absolute"
top={1}
insetInlineEnd={1}
top={2}
insetInlineEnd={2}
/>
{isHovered && <DeleteIcon onClick={handleDelete} />}
@@ -227,8 +223,8 @@ const DeleteIcon = ({ onClick }: { onClick: MouseEventHandler }) => {
icon={<PiTrashSimpleFill size="16px" />}
tooltip={t('gallery.deleteImage_one')}
position="absolute"
bottom={1}
insetInlineEnd={1}
bottom={2}
insetInlineEnd={2}
/>
);
};

View File

@@ -78,24 +78,52 @@ const Content = () => {
// Managing refs for dynamically rendered components is a bit tedious:
// - https://react.dev/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback
// As a easy workaround, we can just grab the first gallery image element directly.
const galleryImageEl = document.querySelector(`.${GALLERY_IMAGE_CLASS_NAME}`);
if (!galleryImageEl) {
const imageEl = document.querySelector(`.${GALLERY_IMAGE_CLASS_NAME}`);
if (!imageEl) {
// No images in gallery?
return;
}
const galleryImageRect = galleryImageEl.getBoundingClientRect();
const gridEl = document.querySelector(`.${GALLERY_GRID_CLASS_NAME}`);
if (!gridEl) {
return;
}
const imageRect = imageEl.getBoundingClientRect();
const containerRect = container.getBoundingClientRect();
if (!galleryImageRect.width || !galleryImageRect.height || !containerRect.width || !containerRect.height) {
// We need to account for the gap between images
const gridElStyle = window.getComputedStyle(gridEl);
const gap = parseFloat(gridElStyle.gap);
if (!imageRect.width || !imageRect.height || !containerRect.width || !containerRect.height) {
// Gallery is too small to fit images or not rendered yet
return;
}
// Floating-point precision requires we round to get the correct number of images per row
const imagesPerRow = Math.round(containerRect.width / galleryImageRect.width);
// However, when calculating the number of images per column, we want to floor the value to not overflow the container
const imagesPerColumn = Math.floor(containerRect.height / galleryImageRect.height);
let imagesPerColumn = 0;
let spaceUsed = 0;
while (spaceUsed + imageRect.height <= containerRect.height) {
imagesPerColumn++; // Increment the number of images
spaceUsed += imageRect.height; // Add image size to the used space
if (spaceUsed + gap <= containerRect.height) {
spaceUsed += gap; // Add gap size to the used space after each image except after the last image
}
}
let imagesPerRow = 0;
spaceUsed = 0;
while (spaceUsed + imageRect.width <= containerRect.width) {
imagesPerRow++; // Increment the number of images
spaceUsed += imageRect.width; // Add image size to the used space
if (spaceUsed + gap <= containerRect.width) {
spaceUsed += gap; // Add gap size to the used space after each image except after the last image
}
}
// Always load at least 1 row of images
const limit = Math.max(imagesPerRow, imagesPerRow * imagesPerColumn);
dispatch(limitChanged(limit));
@@ -139,6 +167,7 @@ const Content = () => {
<Grid
className={GALLERY_GRID_CLASS_NAME}
gridTemplateColumns={`repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr))`}
gap={1}
>
{imageDTOs.map((imageDTO, index) => (
<GalleryImage key={imageDTO.image_name} imageDTO={imageDTO} index={index} />

View File

@@ -4,13 +4,13 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { $activeScopes } from 'common/hooks/interactionScopes';
import { useGalleryImages } from 'features/gallery/hooks/useGalleryImages';
import { selectionChanged } from 'features/gallery/store/gallerySlice';
import { $isGalleryPanelOpen } from 'features/ui/store/uiSlice';
import { $isRightPanelOpen } from 'features/ui/store/uiSlice';
import { computed } from 'nanostores';
import { useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
const $isSelectAllEnabled = computed([$activeScopes, $isGalleryPanelOpen], (activeScopes, isGalleryPanelOpen) => {
const $isSelectAllEnabled = computed([$activeScopes, $isRightPanelOpen], (activeScopes, isGalleryPanelOpen) => {
return activeScopes.has('gallery') && !activeScopes.has('workflows') && isGalleryPanelOpen;
});

View File

@@ -21,7 +21,8 @@ export const ImageViewer = memo(() => {
<Flex
ref={ref}
tabIndex={-1}
layerStyle="body"
layerStyle="first"
p={2}
borderRadius="base"
position="absolute"
flexDirection="column"

View File

@@ -1,61 +0,0 @@
import { Flex, Text } from '@invoke-ai/ui-library';
import { IconSwitch } from 'common/components/IconSwitch';
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
import { memo, useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiEyeBold, PiPencilBold } from 'react-icons/pi';
const TooltipEdit = memo(() => {
const { t } = useTranslation();
return (
<Flex flexDir="column">
<Text fontWeight="semibold">{t('common.edit')}</Text>
<Text fontWeight="normal">{t('common.editDesc')}</Text>
</Flex>
);
});
TooltipEdit.displayName = 'TooltipEdit';
const TooltipView = memo(() => {
const { t } = useTranslation();
return (
<Flex flexDir="column">
<Text fontWeight="semibold">{t('common.view')}</Text>
<Text fontWeight="normal">{t('common.viewDesc')}</Text>
</Flex>
);
});
TooltipView.displayName = 'TooltipView';
export const ViewerToggle = memo(() => {
const imageViewer = useImageViewer();
useHotkeys('z', imageViewer.onToggle, [imageViewer]);
useHotkeys('esc', imageViewer.onClose, [imageViewer]);
const onChange = useCallback(
(isChecked: boolean) => {
if (isChecked) {
imageViewer.onClose();
} else {
imageViewer.onOpen();
}
},
[imageViewer]
);
return (
<IconSwitch
isChecked={!imageViewer.isOpen}
onChange={onChange}
iconUnchecked={<PiEyeBold />}
tooltipUnchecked={<TooltipView />}
iconChecked={<PiPencilBold />}
tooltipChecked={<TooltipEdit />}
ariaLabel="Toggle viewer"
/>
);
});
ViewerToggle.displayName = 'ViewerToggle';

View File

@@ -1,23 +1,11 @@
import { Flex } from '@invoke-ai/ui-library';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import CurrentImageButtons from './CurrentImageButtons';
import { ViewerToggle } from './ViewerToggleMenu';
const selectShowToggle = createSelector(selectActiveTab, (tab) => {
if (tab === 'upscaling' || tab === 'workflows') {
return false;
}
return true;
});
export const ViewerToolbar = memo(() => {
const showToggle = useAppSelector(selectShowToggle);
return (
<Flex w="full" gap={2}>
<Flex flex={1} justifyContent="center">
@@ -30,9 +18,7 @@ export const ViewerToolbar = memo(() => {
<CurrentImageButtons />
</Flex>
<Flex flex={1} justifyContent="center">
<Flex gap={2} marginInlineStart="auto">
{showToggle && <ViewerToggle />}
</Flex>
<Flex gap={2} marginInlineStart="auto" />
</Flex>
</Flex>
);

View File

@@ -1,73 +0,0 @@
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { selectHasImageToCompare, selectIsImageViewerOpen } from 'features/gallery/store/gallerySelectors';
import {
imageToCompareChanged,
isImageViewerOpenChanged,
selectGallerySlice,
} from 'features/gallery/store/gallerySlice';
import { selectWorkflowSlice } from 'features/nodes/store/workflowSlice';
import { selectUiSlice } from 'features/ui/store/uiSlice';
import { useCallback } from 'react';
const selectIsOpen = createSelector(selectUiSlice, selectWorkflowSlice, selectGallerySlice, (ui, workflow, gallery) => {
const tab = ui.activeTab;
const workflowsMode = workflow.mode;
if (tab === 'models' || tab === 'queue') {
return false;
}
if (tab === 'workflows' && workflowsMode === 'edit') {
return false;
}
if (tab === 'workflows' && workflowsMode === 'view') {
return true;
}
if (tab === 'upscaling') {
return true;
}
return gallery.isImageViewerOpen;
});
export const useIsImageViewerOpen = () => {
const isOpen = useAppSelector(selectIsOpen);
return isOpen;
};
const selectIsForcedOpen = createSelector(selectUiSlice, selectWorkflowSlice, (ui, workflow) => {
return ui.activeTab === 'upscaling' || (ui.activeTab === 'workflows' && workflow.mode === 'view');
});
export const useImageViewer = () => {
const dispatch = useAppDispatch();
const isComparing = useAppSelector(selectHasImageToCompare);
const isNaturallyOpen = useAppSelector(selectIsImageViewerOpen);
const isForcedOpen = useAppSelector(selectIsForcedOpen);
const onClose = useCallback(() => {
if (isForcedOpen) {
return;
}
if (isComparing && isNaturallyOpen) {
dispatch(imageToCompareChanged(null));
} else {
dispatch(isImageViewerOpenChanged(false));
}
}, [dispatch, isComparing, isForcedOpen, isNaturallyOpen]);
const onOpen = useCallback(() => {
dispatch(isImageViewerOpenChanged(true));
}, [dispatch]);
const onToggle = useCallback(() => {
if (isForcedOpen) {
return;
}
if (isComparing && isNaturallyOpen) {
dispatch(imageToCompareChanged(null));
} else {
dispatch(isImageViewerOpenChanged(!isNaturallyOpen));
}
}, [dispatch, isComparing, isForcedOpen, isNaturallyOpen]);
return { isOpen: isNaturallyOpen || isForcedOpen, onOpen, onClose, onToggle, isComparing };
};

View File

@@ -5,7 +5,7 @@ import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useGalleryNavigation } from 'features/gallery/hooks/useGalleryNavigation';
import { useGalleryPagination } from 'features/gallery/hooks/useGalleryPagination';
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
import { $isGalleryPanelOpen } from 'features/ui/store/uiSlice';
import { $isRightPanelOpen } from 'features/ui/store/uiSlice';
import { computed } from 'nanostores';
import { useHotkeys } from 'react-hotkeys-hook';
import { useListImagesQuery } from 'services/api/endpoints/images';
@@ -15,7 +15,7 @@ const $leftRightHotkeysEnabled = computed($activeScopes, (activeScopes) => {
return !activeScopes.has('canvas') || activeScopes.has('imageViewer');
});
const $upDownHotkeysEnabled = computed([$activeScopes, $isGalleryPanelOpen], (activeScopes, isGalleryPanelOpen) => {
const $upDownHotkeysEnabled = computed([$activeScopes, $isRightPanelOpen], (activeScopes, isGalleryPanelOpen) => {
// The up and down hotkeys can be used when the gallery is focused and the canvas is not focused, and the gallery panel is open.
return !activeScopes.has('canvas') && isGalleryPanelOpen;
});

View File

@@ -57,4 +57,4 @@ export const selectImageToCompare = createSelector(selectGallerySlice, (gallery)
export const selectHasImageToCompare = createSelector(selectImageToCompare, (imageToCompare) =>
Boolean(imageToCompare)
);
export const selectIsImageViewerOpen = createSelector(selectGallerySlice, (gallery) => gallery.isImageViewerOpen);
export const selectIsMiniViewerOpen = createSelector(selectGallerySlice, (gallery) => gallery.isMiniViewerOpen);

View File

@@ -21,11 +21,11 @@ const initialGalleryState: GalleryState = {
starredFirst: true,
orderDir: 'DESC',
searchTerm: '',
isImageViewerOpen: true,
imageToCompare: null,
comparisonMode: 'slider',
comparisonFit: 'fill',
shouldShowArchivedBoards: false,
isMiniViewerOpen: false,
};
export const gallerySlice = createSlice({
@@ -40,9 +40,6 @@ export const gallerySlice = createSlice({
},
imageToCompareChanged: (state, action: PayloadAction<ImageDTO | null>) => {
state.imageToCompare = action.payload;
if (action.payload) {
state.isImageViewerOpen = true;
}
},
comparisonModeChanged: (state, action: PayloadAction<ComparisonMode>) => {
state.comparisonMode = action.payload;
@@ -91,8 +88,8 @@ export const gallerySlice = createSlice({
alwaysShowImageSizeBadgeChanged: (state, action: PayloadAction<boolean>) => {
state.alwaysShowImageSizeBadge = action.payload;
},
isImageViewerOpenChanged: (state, action: PayloadAction<boolean>) => {
state.isImageViewerOpen = action.payload;
isMiniViewerOpenToggled: (state) => {
state.isMiniViewerOpen = !state.isMiniViewerOpen;
},
comparedImagesSwapped: (state) => {
if (state.imageToCompare) {
@@ -138,7 +135,6 @@ export const {
selectionChanged,
boardSearchTextChanged,
alwaysShowImageSizeBadgeChanged,
isImageViewerOpenChanged,
imageToCompareChanged,
comparisonModeChanged,
comparedImagesSwapped,
@@ -150,6 +146,7 @@ export const {
starredFirstChanged,
shouldShowArchivedBoardsChanged,
searchTermChanged,
isMiniViewerOpenToggled,
} = gallerySlice.actions;
export const selectGallerySlice = (state: RootState) => state.gallery;
@@ -166,13 +163,5 @@ export const galleryPersistConfig: PersistConfig<GalleryState> = {
name: gallerySlice.name,
initialState: initialGalleryState,
migrate: migrateGalleryState,
persistDenylist: [
'selection',
'selectedBoardId',
'galleryView',
'offset',
'limit',
'isImageViewerOpen',
'imageToCompare',
],
persistDenylist: ['selection', 'selectedBoardId', 'galleryView', 'offset', 'limit', 'imageToCompare'],
};

View File

@@ -27,6 +27,6 @@ export type GalleryState = {
imageToCompare: ImageDTO | null;
comparisonMode: ComparisonMode;
comparisonFit: ComparisonFit;
isImageViewerOpen: boolean;
shouldShowArchivedBoards: boolean;
isMiniViewerOpen: boolean;
};