mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): add ability to archive/unarchive boards, add toggle to gallery settings to show/hide archived boards in list
This commit is contained in:
parent
5120a76ce5
commit
68c0aa898f
@ -17,6 +17,8 @@
|
|||||||
},
|
},
|
||||||
"boards": {
|
"boards": {
|
||||||
"addBoard": "Add Board",
|
"addBoard": "Add Board",
|
||||||
|
"archiveBoard": "Archive Board",
|
||||||
|
"archived": "Archived",
|
||||||
"autoAddBoard": "Auto-Add Board",
|
"autoAddBoard": "Auto-Add Board",
|
||||||
"bottomMessage": "Deleting this board and its images will reset any features currently using them.",
|
"bottomMessage": "Deleting this board and its images will reset any features currently using them.",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
@ -36,6 +38,7 @@
|
|||||||
"searchBoard": "Search Boards...",
|
"searchBoard": "Search Boards...",
|
||||||
"selectBoard": "Select a Board",
|
"selectBoard": "Select a Board",
|
||||||
"topMessage": "This board contains images used in the following features:",
|
"topMessage": "This board contains images used in the following features:",
|
||||||
|
"unarchiveBoard": "Unarchive Board",
|
||||||
"uncategorized": "Uncategorized",
|
"uncategorized": "Uncategorized",
|
||||||
"downloadBoard": "Download Board",
|
"downloadBoard": "Download Board",
|
||||||
"imagesWithCount_one": "{{count}} image",
|
"imagesWithCount_one": "{{count}} image",
|
||||||
@ -387,6 +390,7 @@
|
|||||||
"openInViewer": "Open in Viewer",
|
"openInViewer": "Open in Viewer",
|
||||||
"selectAllOnPage": "Select All On Page",
|
"selectAllOnPage": "Select All On Page",
|
||||||
"selectAllOnBoard": "Select All On Board",
|
"selectAllOnBoard": "Select All On Board",
|
||||||
|
"showArchivedBoards": "Show Archived Boards",
|
||||||
"selectForCompare": "Select for Compare",
|
"selectForCompare": "Select for Compare",
|
||||||
"selectAnImageToCompare": "Select an Image to Compare",
|
"selectAnImageToCompare": "Select an Image to Compare",
|
||||||
"slider": "Slider",
|
"slider": "Slider",
|
||||||
|
@ -11,6 +11,7 @@ import { memo, useCallback, useMemo, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
||||||
import { useAddImagesToBoardMutation, useRemoveImagesFromBoardMutation } from 'services/api/endpoints/images';
|
import { useAddImagesToBoardMutation, useRemoveImagesFromBoardMutation } from 'services/api/endpoints/images';
|
||||||
|
import { selectListBoardsQueryArgs } from '../../gallery/store/gallerySelectors';
|
||||||
|
|
||||||
const selectImagesToChange = createMemoizedSelector(
|
const selectImagesToChange = createMemoizedSelector(
|
||||||
selectChangeBoardModalSlice,
|
selectChangeBoardModalSlice,
|
||||||
@ -20,7 +21,8 @@ const selectImagesToChange = createMemoizedSelector(
|
|||||||
const ChangeBoardModal = () => {
|
const ChangeBoardModal = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [selectedBoard, setSelectedBoard] = useState<string | null>();
|
const [selectedBoard, setSelectedBoard] = useState<string | null>();
|
||||||
const { data: boards, isFetching } = useListAllBoardsQuery();
|
const queryArgs = useAppSelector(selectListBoardsQueryArgs);
|
||||||
|
const { data: boards, isFetching } = useListAllBoardsQuery(queryArgs);
|
||||||
const isModalOpen = useAppSelector((s) => s.changeBoardModal.isModalOpen);
|
const isModalOpen = useAppSelector((s) => s.changeBoardModal.isModalOpen);
|
||||||
const imagesToChange = useAppSelector(selectImagesToChange);
|
const imagesToChange = useAppSelector(selectImagesToChange);
|
||||||
const [addImagesToBoard] = useAddImagesToBoardMutation();
|
const [addImagesToBoard] = useAddImagesToBoardMutation();
|
||||||
|
@ -11,25 +11,28 @@ const BoardAutoAddSelect = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||||
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
||||||
const { options, hasBoards } = useListAllBoardsQuery(undefined, {
|
const { options, hasBoards } = useListAllBoardsQuery(
|
||||||
selectFromResult: ({ data }) => {
|
{},
|
||||||
const options: ComboboxOption[] = [
|
{
|
||||||
{
|
selectFromResult: ({ data }) => {
|
||||||
label: t('controlnet.none'),
|
const options: ComboboxOption[] = [
|
||||||
value: 'none',
|
{
|
||||||
},
|
label: t('controlnet.none'),
|
||||||
].concat(
|
value: 'none',
|
||||||
(data ?? []).map(({ board_id, board_name }) => ({
|
},
|
||||||
label: board_name,
|
].concat(
|
||||||
value: board_id,
|
(data ?? []).map(({ board_id, board_name }) => ({
|
||||||
}))
|
label: board_name,
|
||||||
);
|
value: board_id,
|
||||||
return {
|
}))
|
||||||
options,
|
);
|
||||||
hasBoards: options.length > 1,
|
return {
|
||||||
};
|
options,
|
||||||
},
|
hasBoards: options.length > 1,
|
||||||
});
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const onChange = useCallback<ComboboxOnChange>(
|
const onChange = useCallback<ComboboxOnChange>(
|
||||||
(v) => {
|
(v) => {
|
||||||
|
@ -7,12 +7,14 @@ import type { BoardId } from 'features/gallery/store/types';
|
|||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiDownloadBold, PiPlusBold } from 'react-icons/pi';
|
import { PiArchiveBold, PiArchiveFill, PiDownloadBold, PiPlusBold } from 'react-icons/pi';
|
||||||
import { useBulkDownloadImagesMutation } from 'services/api/endpoints/images';
|
import { useBulkDownloadImagesMutation } from 'services/api/endpoints/images';
|
||||||
import { useBoardName } from 'services/api/hooks/useBoardName';
|
import { useBoardName } from 'services/api/hooks/useBoardName';
|
||||||
import type { BoardDTO } from 'services/api/types';
|
import type { BoardDTO } from 'services/api/types';
|
||||||
|
|
||||||
import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems';
|
import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems';
|
||||||
|
import { useUpdateBoardMutation } from '../../../../services/api/endpoints/boards';
|
||||||
|
import { MdArchive, MdUnarchive } from 'react-icons/md';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
board?: BoardDTO;
|
board?: BoardDTO;
|
||||||
@ -30,6 +32,8 @@ const BoardContextMenu = ({ board, board_id, setBoardToDelete, children }: Props
|
|||||||
[board]
|
[board]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [updateBoard] = useUpdateBoardMutation();
|
||||||
|
|
||||||
const isSelectedForAutoAdd = useAppSelector(selectIsSelectedForAutoAdd);
|
const isSelectedForAutoAdd = useAppSelector(selectIsSelectedForAutoAdd);
|
||||||
const boardName = useBoardName(board_id);
|
const boardName = useBoardName(board_id);
|
||||||
const isBulkDownloadEnabled = useFeatureStatus('bulkDownload');
|
const isBulkDownloadEnabled = useFeatureStatus('bulkDownload');
|
||||||
@ -44,13 +48,31 @@ const BoardContextMenu = ({ board, board_id, setBoardToDelete, children }: Props
|
|||||||
bulkDownload({ image_names: [], board_id: board_id });
|
bulkDownload({ image_names: [], board_id: board_id });
|
||||||
}, [board_id, bulkDownload]);
|
}, [board_id, bulkDownload]);
|
||||||
|
|
||||||
|
const handleArchive = useCallback(() => {
|
||||||
|
updateBoard({
|
||||||
|
board_id,
|
||||||
|
changes: { archived: true },
|
||||||
|
});
|
||||||
|
}, [board_id, updateBoard]);
|
||||||
|
|
||||||
|
const handleUnarchive = useCallback(() => {
|
||||||
|
updateBoard({
|
||||||
|
board_id,
|
||||||
|
changes: { archived: false },
|
||||||
|
});
|
||||||
|
}, [board_id, updateBoard]);
|
||||||
|
|
||||||
|
const isBoardArchived = useMemo(() => {
|
||||||
|
return !!board?.archived;
|
||||||
|
}, [board]);
|
||||||
|
|
||||||
const renderMenuFunc = useCallback(
|
const renderMenuFunc = useCallback(
|
||||||
() => (
|
() => (
|
||||||
<MenuList visibility="visible">
|
<MenuList visibility="visible">
|
||||||
<MenuGroup title={boardName}>
|
<MenuGroup title={boardName}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<PiPlusBold />}
|
icon={<PiPlusBold />}
|
||||||
isDisabled={isSelectedForAutoAdd || autoAssignBoardOnClick}
|
isDisabled={isSelectedForAutoAdd || autoAssignBoardOnClick || isBoardArchived}
|
||||||
onClick={handleSetAutoAdd}
|
onClick={handleSetAutoAdd}
|
||||||
>
|
>
|
||||||
{t('boards.menuItemAutoAdd')}
|
{t('boards.menuItemAutoAdd')}
|
||||||
@ -60,6 +82,17 @@ const BoardContextMenu = ({ board, board_id, setBoardToDelete, children }: Props
|
|||||||
{t('boards.downloadBoard')}
|
{t('boards.downloadBoard')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
{board &&
|
||||||
|
(isBoardArchived ? (
|
||||||
|
<MenuItem icon={<PiArchiveBold />} onClick={handleUnarchive}>
|
||||||
|
{t('boards.unarchiveBoard')}
|
||||||
|
</MenuItem>
|
||||||
|
) : (
|
||||||
|
<MenuItem icon={<PiArchiveFill />} onClick={handleArchive}>
|
||||||
|
{t('boards.archiveBoard')}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
|
||||||
{board && <GalleryBoardContextMenuItems board={board} setBoardToDelete={setBoardToDelete} />}
|
{board && <GalleryBoardContextMenuItems board={board} setBoardToDelete={setBoardToDelete} />}
|
||||||
</MenuGroup>
|
</MenuGroup>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
|
@ -3,9 +3,10 @@ import { useGetBoardAssetsTotalQuery, useGetBoardImagesTotalQuery } from 'servic
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
board_id: string;
|
board_id: string;
|
||||||
|
isArchived: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BoardTotalsTooltip = ({ board_id }: Props) => {
|
export const BoardTotalsTooltip = ({ board_id, isArchived }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { imagesTotal } = useGetBoardImagesTotalQuery(board_id, {
|
const { imagesTotal } = useGetBoardImagesTotalQuery(board_id, {
|
||||||
selectFromResult: ({ data }) => {
|
selectFromResult: ({ data }) => {
|
||||||
@ -17,5 +18,5 @@ export const BoardTotalsTooltip = ({ board_id }: Props) => {
|
|||||||
return { assetsTotal: data?.total ?? 0 };
|
return { assetsTotal: data?.total ?? 0 };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return `${t('boards.imagesWithCount', { count: imagesTotal })}, ${t('boards.assetsWithCount', { count: assetsTotal })}`;
|
return `${t('boards.imagesWithCount', { count: imagesTotal })}, ${t('boards.assetsWithCount', { count: assetsTotal })}${isArchived ? ` (${t('boards.archived')})` : ''}`;
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,7 @@ import AddBoardButton from './AddBoardButton';
|
|||||||
import BoardsSearch from './BoardsSearch';
|
import BoardsSearch from './BoardsSearch';
|
||||||
import GalleryBoard from './GalleryBoard';
|
import GalleryBoard from './GalleryBoard';
|
||||||
import NoBoardBoard from './NoBoardBoard';
|
import NoBoardBoard from './NoBoardBoard';
|
||||||
|
import { selectListBoardsQueryArgs } from '../../../store/gallerySelectors';
|
||||||
|
|
||||||
const overlayScrollbarsStyles: CSSProperties = {
|
const overlayScrollbarsStyles: CSSProperties = {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@ -26,7 +27,9 @@ const BoardsList = (props: Props) => {
|
|||||||
const { isOpen } = props;
|
const { isOpen } = props;
|
||||||
const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId);
|
const selectedBoardId = useAppSelector((s) => s.gallery.selectedBoardId);
|
||||||
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
|
const boardSearchText = useAppSelector((s) => s.gallery.boardSearchText);
|
||||||
const { data: boards } = useListAllBoardsQuery();
|
const queryArgs = useAppSelector(selectListBoardsQueryArgs);
|
||||||
|
console.log({ queryArgs });
|
||||||
|
const { data: boards } = useListAllBoardsQuery(queryArgs);
|
||||||
const filteredBoards = boardSearchText
|
const filteredBoards = boardSearchText
|
||||||
? boards?.filter((board) => board.board_name.toLowerCase().includes(boardSearchText.toLowerCase()))
|
? boards?.filter((board) => board.board_name.toLowerCase().includes(boardSearchText.toLowerCase()))
|
||||||
: boards;
|
: boards;
|
||||||
|
@ -12,7 +12,7 @@ import { BoardTotalsTooltip } from 'features/gallery/components/Boards/BoardsLis
|
|||||||
import { autoAddBoardIdChanged, boardIdSelected, selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
import { autoAddBoardIdChanged, boardIdSelected, selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
import { memo, useCallback, useMemo, useState } from 'react';
|
import { memo, useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiImagesSquare } from 'react-icons/pi';
|
import { PiArchiveBold, PiImagesSquare } from 'react-icons/pi';
|
||||||
import { useUpdateBoardMutation } from 'services/api/endpoints/boards';
|
import { useUpdateBoardMutation } from 'services/api/endpoints/boards';
|
||||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||||
import type { BoardDTO } from 'services/api/types';
|
import type { BoardDTO } from 'services/api/types';
|
||||||
@ -33,6 +33,7 @@ interface GalleryBoardProps {
|
|||||||
|
|
||||||
const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps) => {
|
const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const { t } = useTranslation();
|
||||||
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
||||||
const selectIsSelectedForAutoAdd = useMemo(
|
const selectIsSelectedForAutoAdd = useMemo(
|
||||||
() => createSelector(selectGallerySlice, (gallery) => board.board_id === gallery.autoAddBoardId),
|
() => createSelector(selectGallerySlice, (gallery) => board.board_id === gallery.autoAddBoardId),
|
||||||
@ -103,7 +104,7 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
|
|||||||
const handleChange = useCallback((newBoardName: string) => {
|
const handleChange = useCallback((newBoardName: string) => {
|
||||||
setLocalBoardName(newBoardName);
|
setLocalBoardName(newBoardName);
|
||||||
}, []);
|
}, []);
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
return (
|
||||||
<Box w="full" h="full" userSelect="none">
|
<Box w="full" h="full" userSelect="none">
|
||||||
<Flex
|
<Flex
|
||||||
@ -118,7 +119,10 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
|
|||||||
>
|
>
|
||||||
<BoardContextMenu board={board} board_id={board_id} setBoardToDelete={setBoardToDelete}>
|
<BoardContextMenu board={board} board_id={board_id} setBoardToDelete={setBoardToDelete}>
|
||||||
{(ref) => (
|
{(ref) => (
|
||||||
<Tooltip label={<BoardTotalsTooltip board_id={board.board_id} />} openDelay={1000}>
|
<Tooltip
|
||||||
|
label={<BoardTotalsTooltip board_id={board.board_id} isArchived={Boolean(board.archived)} />}
|
||||||
|
openDelay={1000}
|
||||||
|
>
|
||||||
<Flex
|
<Flex
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={handleSelectBoard}
|
onClick={handleSelectBoard}
|
||||||
@ -131,6 +135,25 @@ const GalleryBoard = ({ board, isSelected, setBoardToDelete }: GalleryBoardProps
|
|||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
bg="base.800"
|
bg="base.800"
|
||||||
>
|
>
|
||||||
|
{board.archived && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 1,
|
||||||
|
insetInlineEnd: 2,
|
||||||
|
p: 0,
|
||||||
|
minW: 0,
|
||||||
|
svg: {
|
||||||
|
transitionProperty: 'common',
|
||||||
|
transitionDuration: 'normal',
|
||||||
|
fill: 'base.300',
|
||||||
|
filter: 'drop-shadow(0px 0px 0.1rem var(--invoke-colors-base-800))',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon as={PiArchiveBold} />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
{coverImage?.thumbnail_url ? (
|
{coverImage?.thumbnail_url ? (
|
||||||
<Image
|
<Image
|
||||||
src={coverImage?.thumbnail_url}
|
src={coverImage?.thumbnail_url}
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
autoAssignBoardOnClickChanged,
|
autoAssignBoardOnClickChanged,
|
||||||
setGalleryImageMinimumWidth,
|
setGalleryImageMinimumWidth,
|
||||||
shouldAutoSwitchChanged,
|
shouldAutoSwitchChanged,
|
||||||
|
shouldShowArchivedBoardsChanged,
|
||||||
} from 'features/gallery/store/gallerySlice';
|
} from 'features/gallery/store/gallerySlice';
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
@ -38,6 +39,7 @@ const GallerySettingsPopover = () => {
|
|||||||
const shouldAutoSwitch = useAppSelector((s) => s.gallery.shouldAutoSwitch);
|
const shouldAutoSwitch = useAppSelector((s) => s.gallery.shouldAutoSwitch);
|
||||||
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
const autoAssignBoardOnClick = useAppSelector((s) => s.gallery.autoAssignBoardOnClick);
|
||||||
const alwaysShowImageSizeBadge = useAppSelector((s) => s.gallery.alwaysShowImageSizeBadge);
|
const alwaysShowImageSizeBadge = useAppSelector((s) => s.gallery.alwaysShowImageSizeBadge);
|
||||||
|
const shouldShowArchivedBoards = useAppSelector((s) => s.gallery.shouldShowArchivedBoards);
|
||||||
|
|
||||||
const handleChangeGalleryImageMinimumWidth = useCallback(
|
const handleChangeGalleryImageMinimumWidth = useCallback(
|
||||||
(v: number) => {
|
(v: number) => {
|
||||||
@ -63,6 +65,11 @@ const GallerySettingsPopover = () => {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleChangeShouldShowArchivedBoardsChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => dispatch(shouldShowArchivedBoardsChanged(e.target.checked)),
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover isLazy>
|
<Popover isLazy>
|
||||||
<PopoverTrigger>
|
<PopoverTrigger>
|
||||||
@ -99,6 +106,10 @@ const GallerySettingsPopover = () => {
|
|||||||
<FormLabel>{t('gallery.alwaysShowImageSizeBadge')}</FormLabel>
|
<FormLabel>{t('gallery.alwaysShowImageSizeBadge')}</FormLabel>
|
||||||
<Checkbox isChecked={alwaysShowImageSizeBadge} onChange={handleChangeAlwaysShowImageSizeBadgeChanged} />
|
<Checkbox isChecked={alwaysShowImageSizeBadge} onChange={handleChangeAlwaysShowImageSizeBadgeChanged} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>{t('gallery.showArchivedBoards')}</FormLabel>
|
||||||
|
<Checkbox isChecked={shouldShowArchivedBoards} onChange={handleChangeShouldShowArchivedBoardsChanged} />
|
||||||
|
</FormControl>
|
||||||
</FormControlGroup>
|
</FormControlGroup>
|
||||||
<BoardAutoAddSelect />
|
<BoardAutoAddSelect />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -3,7 +3,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
||||||
import type { ListImagesArgs } from 'services/api/types';
|
import type { ListBoardsArgs, ListImagesArgs } from 'services/api/types';
|
||||||
|
|
||||||
export const selectLastSelectedImage = createMemoizedSelector(
|
export const selectLastSelectedImage = createMemoizedSelector(
|
||||||
selectGallerySlice,
|
selectGallerySlice,
|
||||||
@ -23,3 +23,10 @@ export const selectListImagesQueryArgs = createMemoizedSelector(
|
|||||||
}
|
}
|
||||||
: skipToken
|
: skipToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectListBoardsQueryArgs = createMemoizedSelector(
|
||||||
|
selectGallerySlice,
|
||||||
|
(gallery): ListBoardsArgs => ({
|
||||||
|
archived: gallery.shouldShowArchivedBoards ? true : undefined
|
||||||
|
})
|
||||||
|
);
|
||||||
|
@ -25,6 +25,7 @@ const initialGalleryState: GalleryState = {
|
|||||||
imageToCompare: null,
|
imageToCompare: null,
|
||||||
comparisonMode: 'slider',
|
comparisonMode: 'slider',
|
||||||
comparisonFit: 'fill',
|
comparisonFit: 'fill',
|
||||||
|
shouldShowArchivedBoards: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gallerySlice = createSlice({
|
export const gallerySlice = createSlice({
|
||||||
@ -110,6 +111,9 @@ export const gallerySlice = createSlice({
|
|||||||
limitChanged: (state, action: PayloadAction<number>) => {
|
limitChanged: (state, action: PayloadAction<number>) => {
|
||||||
state.limit = action.payload;
|
state.limit = action.payload;
|
||||||
},
|
},
|
||||||
|
shouldShowArchivedBoardsChanged: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.shouldShowArchivedBoards = action.payload;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addMatcher(isAnyBoardDeleted, (state, action) => {
|
builder.addMatcher(isAnyBoardDeleted, (state, action) => {
|
||||||
@ -154,6 +158,7 @@ export const {
|
|||||||
comparisonModeCycled,
|
comparisonModeCycled,
|
||||||
offsetChanged,
|
offsetChanged,
|
||||||
limitChanged,
|
limitChanged,
|
||||||
|
shouldShowArchivedBoardsChanged,
|
||||||
} = gallerySlice.actions;
|
} = gallerySlice.actions;
|
||||||
|
|
||||||
const isAnyBoardDeleted = isAnyOf(
|
const isAnyBoardDeleted = isAnyOf(
|
||||||
|
@ -25,4 +25,5 @@ export type GalleryState = {
|
|||||||
comparisonMode: ComparisonMode;
|
comparisonMode: ComparisonMode;
|
||||||
comparisonFit: ComparisonFit;
|
comparisonFit: ComparisonFit;
|
||||||
isImageViewerOpen: boolean;
|
isImageViewerOpen: boolean;
|
||||||
|
shouldShowArchivedBoards: boolean;
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
|
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
|
||||||
import { Combobox, FormControl } from '@invoke-ai/ui-library';
|
import { Combobox, FormControl } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { fieldBoardValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldBoardValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import type { BoardFieldInputInstance, BoardFieldInputTemplate } from 'features/nodes/types/field';
|
import type { BoardFieldInputInstance, BoardFieldInputTemplate } from 'features/nodes/types/field';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -8,12 +8,14 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
||||||
|
|
||||||
import type { FieldComponentProps } from './types';
|
import type { FieldComponentProps } from './types';
|
||||||
|
import { selectListBoardsQueryArgs } from '../../../../../../../gallery/store/gallerySelectors';
|
||||||
|
|
||||||
const BoardFieldInputComponent = (props: FieldComponentProps<BoardFieldInputInstance, BoardFieldInputTemplate>) => {
|
const BoardFieldInputComponent = (props: FieldComponentProps<BoardFieldInputInstance, BoardFieldInputTemplate>) => {
|
||||||
const { nodeId, field } = props;
|
const { nodeId, field } = props;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { options, hasBoards } = useListAllBoardsQuery(undefined, {
|
const queryArgs = useAppSelector(selectListBoardsQueryArgs);
|
||||||
|
const { options, hasBoards } = useListAllBoardsQuery(queryArgs, {
|
||||||
selectFromResult: ({ data }) => {
|
selectFromResult: ({ data }) => {
|
||||||
const options: ComboboxOption[] = [
|
const options: ComboboxOption[] = [
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
import { ASSETS_CATEGORIES, IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
||||||
import type { BoardDTO, OffsetPaginatedResults_ImageDTO_, UpdateBoardArg } from 'services/api/types';
|
import type { BoardDTO, ListBoardsArgs, OffsetPaginatedResults_ImageDTO_, UpdateBoardArg } from 'services/api/types';
|
||||||
import { getListImagesUrl } from 'services/api/util';
|
import { getListImagesUrl } from 'services/api/util';
|
||||||
|
|
||||||
import type { ApiTagDescription } from '..';
|
import type { ApiTagDescription } from '..';
|
||||||
@ -18,10 +18,10 @@ export const boardsApi = api.injectEndpoints({
|
|||||||
/**
|
/**
|
||||||
* Boards Queries
|
* Boards Queries
|
||||||
*/
|
*/
|
||||||
listAllBoards: build.query<Array<BoardDTO>, void>({
|
listAllBoards: build.query<Array<BoardDTO>, ListBoardsArgs>({
|
||||||
query: () => ({
|
query: (args) => ({
|
||||||
url: buildBoardsUrl(),
|
url: buildBoardsUrl(),
|
||||||
params: { all: true },
|
params: { all: true, ...args },
|
||||||
}),
|
}),
|
||||||
providesTags: (result) => {
|
providesTags: (result) => {
|
||||||
// any list of boards
|
// any list of boards
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import type { BoardId } from 'features/gallery/store/types';
|
import type { BoardId } from 'features/gallery/store/types';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
||||||
|
import { selectListBoardsQueryArgs } from '../../../features/gallery/store/gallerySelectors';
|
||||||
|
|
||||||
export const useBoardName = (board_id: BoardId) => {
|
export const useBoardName = (board_id: BoardId) => {
|
||||||
const { boardName } = useListAllBoardsQuery(undefined, {
|
const { boardName } = useListAllBoardsQuery({}, {
|
||||||
selectFromResult: ({ data }) => {
|
selectFromResult: ({ data }) => {
|
||||||
const selectedBoard = data?.find((b) => b.board_id === board_id);
|
const selectedBoard = data?.find((b) => b.board_id === board_id);
|
||||||
const boardName = selectedBoard?.board_name || t('boards.uncategorized');
|
const boardName = selectedBoard?.board_name || t('boards.uncategorized');
|
||||||
|
File diff suppressed because one or more lines are too long
@ -6,6 +6,8 @@ export type S = components['schemas'];
|
|||||||
export type ListImagesArgs = NonNullable<paths['/api/v1/images/']['get']['parameters']['query']>;
|
export type ListImagesArgs = NonNullable<paths['/api/v1/images/']['get']['parameters']['query']>;
|
||||||
export type ListImagesResponse = paths['/api/v1/images/']['get']['responses']['200']['content']['application/json'];
|
export type ListImagesResponse = paths['/api/v1/images/']['get']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
export type ListBoardsArgs = NonNullable<paths['/api/v1/boards/']['get']['parameters']['query']>;
|
||||||
|
|
||||||
export type DeleteBoardResult =
|
export type DeleteBoardResult =
|
||||||
paths['/api/v1/boards/{board_id}']['delete']['responses']['200']['content']['application/json'];
|
paths['/api/v1/boards/{board_id}']['delete']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user