diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/AddBoardButton.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/AddBoardButton.tsx
index 2f60ea2dac..d8828fe736 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/AddBoardButton.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/AddBoardButton.tsx
@@ -1,9 +1,19 @@
import { Flex, Icon, Text } from '@chakra-ui/react';
+import { useCallback } from 'react';
import { FaPlus } from 'react-icons/fa';
+import { useAppDispatch } from '../../../../app/store/storeHooks';
+import { boardCreated } from '../../../../services/thunks/board';
const AddBoardButton = () => {
+ const dispatch = useAppDispatch();
+
+ const handleCreateBoard = useCallback(() => {
+ dispatch(boardCreated({ requestBody: 'My Board' }));
+ }, [dispatch]);
+
return (
{
+ const dispatch = useDispatch();
+
+ const handleAllImagesBoardClick = () => {
+ dispatch(boardIdSelected(null));
+ };
+
+ return (
+
+
+
+
+ All Images
+
+ );
+};
+
+export default AllImagesBoard;
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx
index 064d40b2d3..8603f28c9c 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx
@@ -2,22 +2,26 @@ import { Grid } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
-import { selectBoardsAll } from 'features/gallery/store/boardSlice';
-import { memo } from 'react';
+import {
+ boardsSelector,
+ selectBoardsAll,
+} from 'features/gallery/store/boardSlice';
+import { memo, useState } from 'react';
import HoverableBoard from './HoverableBoard';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import AddBoardButton from './AddBoardButton';
+import AllImagesBoard from './AllImagesBoard';
const selector = createSelector(
- selectBoardsAll,
- (boards) => {
- return { boards };
+ [selectBoardsAll, boardsSelector],
+ (boards, boardsState) => {
+ return { boards, selectedBoardId: boardsState.selectedBoardId };
},
defaultSelectorOptions
);
const BoardsList = () => {
- const { boards } = useAppSelector(selector);
+ const { boards, selectedBoardId } = useAppSelector(selector);
return (
{
}}
>
+
{boards.map((board) => (
-
+
))}
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx
index fa1caeac7a..4bb63dbb5e 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx
@@ -1,49 +1,51 @@
import {
Box,
+ Editable,
+ EditableInput,
+ EditablePreview,
Flex,
Icon,
Image,
MenuItem,
MenuList,
- Text,
} from '@chakra-ui/react';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { PropsWithChildren, memo, useCallback, useState } from 'react';
-import { FaFolder, FaImage } from 'react-icons/fa';
+import { useAppDispatch } from 'app/store/storeHooks';
+import { memo, useCallback } from 'react';
+import { FaFolder, FaTrash } from 'react-icons/fa';
import { ContextMenu } from 'chakra-ui-contextmenu';
import { useTranslation } from 'react-i18next';
-import { ExternalLinkIcon } from '@chakra-ui/icons';
-import { useAppToaster } from 'app/components/Toaster';
import { BoardDTO } from 'services/api';
-import { EntityId, createSelector } from '@reduxjs/toolkit';
-import {
- selectFilteredImagesIds,
- selectImagesById,
-} from '../../store/imagesSlice';
-import { RootState } from '../../../../app/store/store';
-import { defaultSelectorOptions } from '../../../../app/store/util/defaultMemoizeOptions';
-import { useSelector } from 'react-redux';
import { IAIImageFallback } from 'common/components/IAIImageFallback';
import { boardIdSelected } from 'features/gallery/store/boardSlice';
+import { boardDeleted, boardUpdated } from '../../../../services/thunks/board';
interface HoverableBoardProps {
board: BoardDTO;
+ isSelected: boolean;
}
-/**
- * Gallery image component with delete/use all/use seed buttons on hover.
- */
-const HoverableBoard = memo(({ board }: HoverableBoardProps) => {
+const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => {
const dispatch = useAppDispatch();
const { board_name, board_id, cover_image_url } = board;
- const { t } = useTranslation();
-
const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(board_id));
}, [board_id, dispatch]);
+ const handleDeleteBoard = useCallback(() => {
+ dispatch(boardDeleted(board_id));
+ }, [board_id, dispatch]);
+
+ const handleUpdateBoardName = (newBoardName: string) => {
+ dispatch(
+ boardUpdated({
+ boardId: board_id,
+ requestBody: { board_name: newBoardName },
+ })
+ );
+ };
+
return (
@@ -51,10 +53,11 @@ const HoverableBoard = memo(({ board }: HoverableBoardProps) => {
renderMenu={() => (
}
- // onClickCapture={handleOpenInNewTab}
+ sx={{ color: 'error.300' }}
+ icon={}
+ onClickCapture={handleDeleteBoard}
>
- Sample Menu Item
+ Delete Board
)}
@@ -64,7 +67,6 @@ const HoverableBoard = memo(({ board }: HoverableBoardProps) => {
position="relative"
key={board_id}
userSelect="none"
- onClick={handleSelectBoard}
ref={ref}
sx={{
flexDir: 'column',
@@ -77,12 +79,13 @@ const HoverableBoard = memo(({ board }: HoverableBoardProps) => {
}}
>
{
)}
- {board_name}
+
+ {
+ handleUpdateBoardName(nextValue);
+ }}
+ >
+
+
+
)}
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx
index d97d814adf..95c37cba61 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx
@@ -37,7 +37,7 @@ import {
} from 'react';
import { useTranslation } from 'react-i18next';
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
-import { FaFolder, FaImage, FaPlus, FaServer, FaWrench } from 'react-icons/fa';
+import { FaImage, FaServer, FaWrench } from 'react-icons/fa';
import { MdPhotoLibrary } from 'react-icons/md';
import HoverableImage from './HoverableImage';
@@ -55,10 +55,6 @@ import {
} from '../store/imagesSlice';
import { receivedPageOfImages } from 'services/thunks/image';
import { boardSelector } from '../store/boardSelectors';
-import { BoardDTO, ImageDTO } from '../../../services/api';
-import { isBoardDTO, isImageDTO } from '../../../services/types/guards';
-import HoverableBoard from './Boards/HoverableBoard';
-import IAIInput from '../../../common/components/IAIInput';
import { boardCreated } from '../../../services/thunks/board';
import BoardsList from './Boards/BoardsList';
import { selectBoardsById } from '../store/boardSlice';
@@ -66,18 +62,16 @@ import { selectBoardsById } from '../store/boardSlice';
const itemSelector = createSelector(
[(state: RootState) => state],
(state) => {
- const { images, boards, gallery } = state;
-
- let items: Array = [];
- let areMoreAvailable = false;
- let isLoading = true;
+ const { images, boards } = state;
const { categories } = images;
const allImages = selectImagesAll(state);
- items = allImages.filter((i) => categories.includes(i.image_category));
- areMoreAvailable = items.length < images.total;
- isLoading = images.isLoading;
+ const items = allImages.filter((i) =>
+ categories.includes(i.image_category)
+ );
+ const areMoreAvailable = items.length < images.total;
+ const isLoading = images.isLoading;
const selectedBoard = boards.selectedBoardId
? selectBoardsById(state, boards.selectedBoardId)
@@ -353,27 +347,17 @@ const ImageGalleryContent = () => {
data={items}
endReached={handleEndReached}
scrollerRef={(ref) => setScrollerRef(ref)}
- itemContent={(index, item) => {
- if (isImageDTO(item)) {
- return (
-
-
-
- );
- } else if (isBoardDTO(item)) {
- return (
-
-
-
- );
- }
- }}
+ itemContent={(index, item) => (
+
+
+
+ )}
/>
) : (
{
List: ListContainer,
}}
scrollerRef={setScroller}
- itemContent={(index, item) => {
- if (isImageDTO(item)) {
- return (
-
- );
- } else if (isBoardDTO(item)) {
- return (
-
- );
- }
- }}
+ itemContent={(index, item) => (
+
+ )}
/>
)}
diff --git a/invokeai/frontend/web/src/features/gallery/store/boardSlice.ts b/invokeai/frontend/web/src/features/gallery/store/boardSlice.ts
index de7ea1e828..d2e9a451d3 100644
--- a/invokeai/frontend/web/src/features/gallery/store/boardSlice.ts
+++ b/invokeai/frontend/web/src/features/gallery/store/boardSlice.ts
@@ -8,7 +8,12 @@ import {
import { RootState } from 'app/store/store';
import { BoardDTO } from 'services/api';
import { dateComparator } from 'common/util/dateComparator';
-import { receivedBoards } from '../../../services/thunks/board';
+import {
+ boardCreated,
+ boardDeleted,
+ boardUpdated,
+ receivedBoards,
+} from '../../../services/thunks/board';
export const boardsAdapter = createEntityAdapter({
selectId: (board) => board.board_id,
@@ -26,7 +31,7 @@ type AdditionalBoardsState = {
export const initialBoardsState =
boardsAdapter.getInitialState({
offset: 0,
- limit: 0,
+ limit: 50,
total: 0,
isLoading: false,
selectedBoardId: null,
@@ -47,7 +52,7 @@ const boardsSlice = createSlice({
boardRemoved: (state, action: PayloadAction) => {
boardsAdapter.removeOne(state, action.payload);
},
- boardIdSelected: (state, action: PayloadAction) => {
+ boardIdSelected: (state, action: PayloadAction) => {
state.selectedBoardId = action.payload;
},
},
@@ -66,6 +71,19 @@ const boardsSlice = createSlice({
state.total = total;
boardsAdapter.upsertMany(state, items);
});
+ builder.addCase(boardCreated.fulfilled, (state, action) => {
+ const board = action.payload;
+ boardsAdapter.upsertOne(state, board);
+ });
+ builder.addCase(boardUpdated.fulfilled, (state, action) => {
+ const board = action.payload;
+ boardsAdapter.upsertOne(state, board);
+ });
+ builder.addCase(boardDeleted.pending, (state, action) => {
+ const boardId = action.meta.arg;
+ console.log({ boardId });
+ boardsAdapter.removeOne(state, boardId);
+ });
},
});
diff --git a/invokeai/frontend/web/src/services/thunks/board.ts b/invokeai/frontend/web/src/services/thunks/board.ts
index 4ead87c4d4..a536a3fdb0 100644
--- a/invokeai/frontend/web/src/services/thunks/board.ts
+++ b/invokeai/frontend/web/src/services/thunks/board.ts
@@ -21,3 +21,21 @@ export const boardCreated = createAppAsyncThunk(
return response;
}
);
+
+export const boardDeleted = createAppAsyncThunk(
+ 'api/boardDeleted',
+ async (boardId: string) => {
+ await BoardsService.deleteBoard({ boardId });
+ return boardId;
+ }
+);
+
+type BoardUpdatedArg = Parameters<(typeof BoardsService)['updateBoard']>[0];
+
+export const boardUpdated = createAppAsyncThunk(
+ 'api/boardUpdated',
+ async (arg: BoardUpdatedArg) => {
+ const response = await BoardsService.updateBoard(arg);
+ return response;
+ }
+);