mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
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:
parent
f42ef55b2f
commit
af4b845a9f
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 }}>
|
||||
|
@ -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(
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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 = (
|
||||
|
@ -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' },
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -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());
|
||||
|
@ -26,6 +26,7 @@ const assetsQueryArg: ListImagesArgs = {
|
||||
|
||||
const noBoardQueryArg: ListImagesArgs = {
|
||||
board_id: 'none',
|
||||
categories: IMAGE_CATEGORIES,
|
||||
...baseQueryArg,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user