changes: assets cannot be on boards, move no boards back to boards list, update all queries to have categories bc never fetching both images and assets for a board

This commit is contained in:
Mary Hipp 2023-07-20 15:24:12 -04:00
parent f42ef55b2f
commit af4b845a9f
11 changed files with 153 additions and 120 deletions

View File

@ -44,33 +44,8 @@ export const addImageUploadedFulfilledListener = () => {
// default action - just upload and alert user
if (postUploadAction?.type === 'TOAST') {
const { toastOptions } = postUploadAction;
if (SYSTEM_BOARDS.includes(selectedBoardId)) {
dispatch(addToast({ ...DEFAULT_UPLOADED_TOAST, ...toastOptions }));
} else {
// Add this image to the board
dispatch(
imagesApi.endpoints.addImageToBoard.initiate({
board_id: selectedBoardId,
imageDTO,
})
);
dispatch(addToast({ ...DEFAULT_UPLOADED_TOAST, ...toastOptions }));
// Attempt to get the board's name for the toast
const { data } = boardsApi.endpoints.listAllBoards.select()(state);
// Fall back to just the board id if we can't find the board for some reason
const board = data?.find((b) => b.board_id === selectedBoardId);
const description = board
? `Added to board ${board.board_name}`
: `Added to board ${selectedBoardId}`;
dispatch(
addToast({
...DEFAULT_UPLOADED_TOAST,
description,
})
);
}
return;
}

View File

@ -3,11 +3,8 @@ import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIIconButton from 'common/components/IAIIconButton';
import { AnimatePresence, motion } from 'framer-motion';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { memo, useCallback, useState } from 'react';
import { FaSearch } from 'react-icons/fa';
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
import { BoardDTO } from 'services/api/types';
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
@ -16,6 +13,7 @@ import AddBoardButton from './AddBoardButton';
import BoardsSearch from './BoardsSearch';
import GalleryBoard from './GalleryBoard';
import SystemBoardButton from './SystemBoardButton';
import NoBoardBoard from './NoBoardBoard';
const selector = createSelector(
[stateSelector],
@ -42,13 +40,9 @@ const BoardsList = (props: Props) => {
)
: boards;
const [boardToDelete, setBoardToDelete] = useState<BoardDTO>();
const [isSearching, setIsSearching] = useState(false);
const handleClickSearchIcon = useCallback(() => {
setIsSearching((v) => !v);
}, []);
const showBoardList = useCallback(() => {
return selectedBoardId !== 'no_board' && selectedBoardId !== 'assets';
return selectedBoardId !== 'assets';
}, [selectedBoardId]);
return (
@ -68,13 +62,12 @@ const BoardsList = (props: Props) => {
<ButtonGroup sx={{ w: 'full', ps: 1.5 }} isAttached>
<SystemBoardButton board_id="images" />
<SystemBoardButton board_id="assets" />
<SystemBoardButton board_id="no_board" />
</ButtonGroup>
</Flex>
{showBoardList() && (
<>
<Flex sx={{ gap: 2, alignItems: 'center' }}>
<BoardsSearch setIsSearching={setIsSearching} />
<BoardsSearch />
<AddBoardButton />
</Flex>
@ -97,6 +90,9 @@ const BoardsList = (props: Props) => {
maxH: 346,
}}
>
<GridItem key="no_board" sx={{ p: 1.5 }}>
<NoBoardBoard isSelected={selectedBoardId === 'no_board'} />
</GridItem>
{filteredBoards &&
filteredBoards.map((board) => (
<GridItem key={board.board_id} sx={{ p: 1.5 }}>

View File

@ -29,7 +29,7 @@ const selector = createSelector(
);
type Props = {
setIsSearching: (isSearching: boolean) => void;
setIsSearching?: (isSearching: boolean) => void;
};
const BoardsSearch = (props: Props) => {
@ -47,7 +47,7 @@ const BoardsSearch = (props: Props) => {
const clearBoardSearch = useCallback(() => {
dispatch(setBoardSearchText(''));
setIsSearching(false);
setIsSearching && setIsSearching(false);
}, [dispatch, setIsSearching]);
const handleKeydown = useCallback(

View File

@ -30,7 +30,7 @@ const AUTO_ADD_BADGE_STYLES: ChakraProps['sx'] = {
color: 'blackAlpha.900',
};
const BASE_BADGE_STYLES: ChakraProps['sx'] = {
export const BASE_BADGE_STYLES: ChakraProps['sx'] = {
bg: 'base.500',
color: 'whiteAlpha.900',
};

View File

@ -1,10 +1,11 @@
import { As, Badge, Flex } from '@chakra-ui/react';
import { As, Badge, Flex, Box, Icon } from '@chakra-ui/react';
import { TypesafeDroppableData } from 'app/components/ImageDnd/typesafeDnd';
import IAIDroppable from 'common/components/IAIDroppable';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import { BoardId } from 'features/gallery/store/gallerySlice';
import { ReactNode } from 'react';
import { BoardId, boardIdSelected } from 'features/gallery/store/gallerySlice';
import { ReactNode, useCallback } from 'react';
import BoardContextMenu from '../BoardContextMenu';
import { useAppDispatch } from '../../../../../app/store/storeHooks';
import { BASE_BADGE_STYLES } from './GalleryBoard';
import { MdFolderOff } from 'react-icons/md';
type GenericBoardProps = {
board_id: BoardId;
@ -24,82 +25,136 @@ export const formatBadgeCount = (count: number) =>
}).format(count);
const GenericBoard = (props: GenericBoardProps) => {
const {
board_id,
droppableData,
onClick,
isSelected,
icon,
label,
badgeCount,
dropLabel,
} = props;
const { board_id, isSelected, label, badgeCount } = props;
const dispatch = useAppDispatch();
const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(board_id));
}, [board_id, dispatch]);
return (
<BoardContextMenu board_id={board_id}>
{(ref) => (
<Flex
ref={ref}
sx={{
flexDir: 'column',
justifyContent: 'space-between',
alignItems: 'center',
cursor: 'pointer',
w: 'full',
h: 'full',
borderRadius: 'base',
}}
<Box
sx={{ w: 'full', h: 'full', touchAction: 'none', userSelect: 'none' }}
>
<Flex
onClick={onClick}
sx={{
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 'base',
w: 'full',
aspectRatio: '1/1',
overflow: 'hidden',
shadow: isSelected ? 'selected.light' : undefined,
_dark: { shadow: isSelected ? 'selected.dark' : undefined },
flexShrink: 0,
w: 'full',
h: 'full',
}}
>
<IAINoContentFallback
boxSize={8}
icon={icon}
sx={{
border: '2px solid var(--invokeai-colors-base-200)',
_dark: { border: '2px solid var(--invokeai-colors-base-800)' },
}}
/>
<Flex
ref={ref}
onClick={handleSelectBoard}
sx={{
position: 'absolute',
insetInlineEnd: 0,
top: 0,
p: 1,
w: 'full',
h: 'full',
position: 'relative',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 'base',
cursor: 'pointer',
}}
>
{badgeCount !== undefined && (
<Badge variant="solid">{formatBadgeCount(badgeCount)}</Badge>
)}
<Flex
sx={{
w: 'full',
h: 'full',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 'base',
bg: 'base.200',
_dark: {
bg: 'base.800',
},
}}
>
<Flex
sx={{
w: 'full',
h: 'full',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Icon
boxSize={12}
as={MdFolderOff}
sx={{
mt: -3,
opacity: 0.7,
color: 'base.500',
_dark: {
color: 'base.500',
},
}}
/>
</Flex>
</Flex>
<Flex
sx={{
position: 'absolute',
insetInlineEnd: 0,
top: 0,
p: 1,
}}
>
<Badge variant="solid" sx={BASE_BADGE_STYLES}>
{badgeCount}
</Badge>
</Flex>
<Box
className="selection-box"
sx={{
position: 'absolute',
top: 0,
insetInlineEnd: 0,
bottom: 0,
insetInlineStart: 0,
borderRadius: 'base',
transitionProperty: 'common',
transitionDuration: 'common',
shadow: isSelected ? 'selected.light' : undefined,
_dark: {
shadow: isSelected ? 'selected.dark' : undefined,
},
}}
/>
<Flex
sx={{
position: 'absolute',
bottom: 0,
left: 0,
p: 1,
justifyContent: 'center',
alignItems: 'center',
w: 'full',
maxW: 'full',
borderBottomRadius: 'base',
bg: isSelected ? 'accent.400' : 'base.500',
color: isSelected ? 'base.50' : 'base.100',
_dark: {
bg: isSelected ? 'accent.500' : 'base.600',
color: isSelected ? 'base.50' : 'base.100',
},
lineHeight: 'short',
fontSize: 'xs',
}}
>
{label}
</Flex>
</Flex>
<IAIDroppable data={droppableData} dropLabel={dropLabel} />
</Flex>
<Flex
sx={{
h: 'full',
alignItems: 'center',
fontWeight: isSelected ? 600 : undefined,
fontSize: 'sm',
color: isSelected ? 'base.900' : 'base.700',
_dark: { color: isSelected ? 'base.50' : 'base.200' },
}}
>
{label}
</Flex>
</Flex>
</Box>
)}
</BoardContextMenu>
);

View File

@ -1,6 +1,7 @@
import { Text } from '@chakra-ui/react';
import { MoveBoardDropData } from 'app/components/ImageDnd/typesafeDnd';
import {
IMAGE_CATEGORIES,
INITIAL_IMAGE_LIMIT,
boardIdSelected,
} from 'features/gallery/store/gallerySlice';
@ -14,6 +15,7 @@ import GenericBoard from './GenericBoard';
const baseQueryArg: ListImagesArgs = {
board_id: 'none',
categories: IMAGE_CATEGORIES,
offset: 0,
limit: INITIAL_IMAGE_LIMIT,
is_intermediate: false,

View File

@ -8,7 +8,10 @@ import {
resizeAndScaleCanvas,
setInitialCanvasImage,
} from 'features/canvas/store/canvasSlice';
import { imagesAddedToBatch } from 'features/gallery/store/gallerySlice';
import {
IMAGE_CATEGORIES,
imagesAddedToBatch,
} from 'features/gallery/store/gallerySlice';
import { imageToDeleteSelected } from 'features/imageDeletion/store/imageDeletionSlice';
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
import { initialImageSelected } from 'features/parameters/store/actions';
@ -208,11 +211,17 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
Add to Batch
</MenuItem>
)}
<MenuItem icon={<FaFolder />} onClickCapture={handleAddToBoard}>
{imageDTO.board_id ? 'Change Board' : 'Add to Board'}
</MenuItem>
{imageDTO.board_id && (
<MenuItem icon={<FaFolder />} onClickCapture={handleRemoveFromBoard}>
{IMAGE_CATEGORIES.includes(imageDTO.image_category) && (
<MenuItem icon={<FaFolder />} onClickCapture={handleAddToBoard}>
{imageDTO.board_id ? 'Change Board' : 'Add to Board'}
</MenuItem>
)}
{IMAGE_CATEGORIES.includes(imageDTO.image_category) && (
<MenuItem
icon={<FaFolder />}
isDisabled={!imageDTO.board_id}
onClickCapture={handleRemoveFromBoard}
>
Remove from Board
</MenuItem>
)}

View File

@ -10,12 +10,7 @@ export const getCategoriesQueryParamForBoard = (
return ASSETS_CATEGORIES;
}
if (board_id === 'images') {
return IMAGE_CATEGORIES;
}
// 'no_board' board, 'batch' board, user boards
return undefined;
return IMAGE_CATEGORIES;
};
export const getBoardIdQueryParamForBoard = (

View File

@ -157,7 +157,7 @@ export const boardsApi = api.injectEndpoints({
const updates: Update<ImageDTO>[] = deleted_board_images.map(
(image_name) => ({
id: image_name,
changes: { board_id: undefined },
changes: { board_id: 'none' },
})
);

View File

@ -51,7 +51,7 @@ export const imagesSelectors = imagesAdapter.getSelectors();
export const getListImagesUrl = (queryArgs: ListImagesArgs) =>
`images/?${queryString.stringify(queryArgs, { arrayFormat: 'none' })}`;
export const SYSTEM_BOARDS = ['images', 'assets', 'no_board', 'batch'];
export const SYSTEM_BOARDS = ['images', 'assets', 'batch'];
export const imagesApi = api.injectEndpoints({
endpoints: (build) => ({
@ -437,11 +437,11 @@ export const imagesApi = api.injectEndpoints({
const removeFromQueryArgs: ListImagesArgs[] = [];
// remove from "No Board"
removeFromQueryArgs.push({ board_id: 'none' });
removeFromQueryArgs.push({ board_id: 'none', categories: IMAGE_CATEGORIES });
// remove from old board
if (old_board_id) {
removeFromQueryArgs.push({ board_id: old_board_id });
removeFromQueryArgs.push({ board_id: old_board_id, categories: IMAGE_CATEGORIES });
}
// Store all patch results in case we need to roll back
@ -484,7 +484,7 @@ export const imagesApi = api.injectEndpoints({
// We only need to add to the cache if the board is not a system board
if (!SYSTEM_BOARDS.includes(board_id)) {
const queryArgs = { board_id };
const queryArgs = { board_id, categories: IMAGE_CATEGORIES };
const { data } = imagesApi.endpoints.listImages.select(queryArgs)(
getState()
);
@ -569,7 +569,7 @@ export const imagesApi = api.injectEndpoints({
// Remove from old board
if (old_board_id) {
const oldBoardQueryArgs = { board_id: old_board_id };
const oldBoardQueryArgs = { board_id: old_board_id, categories: IMAGE_CATEGORIES };
patches.push(
dispatch(
imagesApi.util.updateQueryData(
@ -588,7 +588,7 @@ export const imagesApi = api.injectEndpoints({
}
// Add to "No Board"
const noBoardQueryArgs = { board_id: 'none' };
const noBoardQueryArgs = { board_id: 'none', categories: IMAGE_CATEGORIES };
const { data } = imagesApi.endpoints.listImages.select(
noBoardQueryArgs
)(getState());

View File

@ -26,6 +26,7 @@ const assetsQueryArg: ListImagesArgs = {
const noBoardQueryArg: ListImagesArgs = {
board_id: 'none',
categories: IMAGE_CATEGORIES,
...baseQueryArg,
};