diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts
index 928deb6933..007a829bf9 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts
@@ -8,6 +8,7 @@ import {
} from 'features/gallery/store/gallerySlice';
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
import { CANVAS_OUTPUT } from 'features/nodes/util/graphBuilders/constants';
+import { boardsApi } from 'services/api/endpoints/boards';
import { imagesApi } from 'services/api/endpoints/images';
import { isImageOutput } from 'services/api/guards';
import { imagesAdapter } from 'services/api/util';
@@ -70,11 +71,21 @@ export const addInvocationCompleteEventListener = () => {
)
);
+ // update the total images for the board
+ dispatch(
+ boardsApi.util.updateQueryData(
+ 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ draft.total += 1;
+ }
+ )
+ );
+
dispatch(
imagesApi.util.invalidateTags([
- { type: 'BoardImagesTotal', id: imageDTO.board_id },
- { type: 'BoardAssetsTotal', id: imageDTO.board_id },
- { type: 'Board', id: imageDTO.board_id },
+ { type: 'Board', id: imageDTO.board_id ?? 'none' },
])
);
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx
index 104512a9c6..33f5a7c5b3 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GalleryBoard.tsx
@@ -77,12 +77,12 @@ const GalleryBoard = ({
const { data: imagesTotal } = useGetBoardImagesTotalQuery(board.board_id);
const { data: assetsTotal } = useGetBoardAssetsTotalQuery(board.board_id);
const tooltip = useMemo(() => {
- if (!imagesTotal || !assetsTotal) {
+ if (!imagesTotal?.total || !assetsTotal?.total) {
return undefined;
}
- return `${imagesTotal} image${
- imagesTotal > 1 ? 's' : ''
- }, ${assetsTotal} asset${assetsTotal > 1 ? 's' : ''}`;
+ return `${imagesTotal.total} image${imagesTotal.total > 1 ? 's' : ''}, ${
+ assetsTotal.total
+ } asset${assetsTotal.total > 1 ? 's' : ''}`;
}, [assetsTotal, imagesTotal]);
const { currentData: coverImage } = useGetImageDTOQuery(
diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx
index 6cea7d3eac..2d2d116928 100644
--- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx
@@ -1,4 +1,4 @@
-import { Box, Flex, Image, Text } from '@chakra-ui/react';
+import { Box, Flex, Image, Text, Tooltip } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
@@ -15,6 +15,10 @@ import { memo, useCallback, useMemo, useState } from 'react';
import { useBoardName } from 'services/api/hooks/useBoardName';
import AutoAddIcon from '../AutoAddIcon';
import BoardContextMenu from '../BoardContextMenu';
+import {
+ useGetBoardAssetsTotalQuery,
+ useGetBoardImagesTotalQuery,
+} from 'services/api/endpoints/boards';
interface Props {
isSelected: boolean;
@@ -41,6 +45,17 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
}, [dispatch, autoAssignBoardOnClick]);
const [isHovered, setIsHovered] = useState(false);
+ const { data: imagesTotal } = useGetBoardImagesTotalQuery('none');
+ const { data: assetsTotal } = useGetBoardAssetsTotalQuery('none');
+ const tooltip = useMemo(() => {
+ if (!imagesTotal?.total || !assetsTotal?.total) {
+ return undefined;
+ }
+ return `${imagesTotal.total} image${imagesTotal.total > 1 ? 's' : ''}, ${
+ assetsTotal.total
+ } asset${assetsTotal.total > 1 ? 's' : ''}`;
+ }, [assetsTotal, imagesTotal]);
+
const handleMouseOver = useCallback(() => {
setIsHovered(true);
}, []);
@@ -74,77 +89,82 @@ const NoBoardBoard = memo(({ isSelected }: Props) => {
>
{(ref) => (
-
+
-
+
+
+ {autoAddBoardId === 'none' && }
+
+ {boardName}
+
+
+ Move}
/>
- {autoAddBoardId === 'none' && }
-
- {boardName}
-
-
- Move}
- />
-
+
)}
diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts b/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts
index 670dd7ee9f..fe30f0a7a7 100644
--- a/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts
+++ b/invokeai/frontend/web/src/features/gallery/hooks/useNextPrevImage.ts
@@ -20,7 +20,7 @@ export const nextPrevImageButtonsSelector = createSelector(
const { data, status } =
imagesApi.endpoints.listImages.select(baseQueryArgs)(state);
- const { data: total } =
+ const { data: totalsData } =
state.gallery.galleryView === 'images'
? boardsApi.endpoints.getBoardImagesTotal.select(
baseQueryArgs.board_id ?? 'none'
@@ -34,7 +34,7 @@ export const nextPrevImageButtonsSelector = createSelector(
const isFetching = status === 'pending';
- if (!data || !lastSelectedImage || total === 0) {
+ if (!data || !lastSelectedImage || totalsData?.total === 0) {
return {
isFetching,
queryArgs: baseQueryArgs,
@@ -74,7 +74,7 @@ export const nextPrevImageButtonsSelector = createSelector(
return {
loadedImagesCount: images.length,
currentImageIndex,
- areMoreImagesAvailable: (total ?? 0) > imagesLength,
+ areMoreImagesAvailable: (totalsData?.total ?? 0) > imagesLength,
isFetching: status === 'pending',
nextImage,
prevImage,
diff --git a/invokeai/frontend/web/src/services/api/endpoints/boards.ts b/invokeai/frontend/web/src/services/api/endpoints/boards.ts
index 53b3bfd59d..2e269be124 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/boards.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/boards.ts
@@ -70,7 +70,7 @@ export const boardsApi = api.injectEndpoints({
keepUnusedDataFor: 0,
}),
- getBoardImagesTotal: build.query({
+ getBoardImagesTotal: build.query<{ total: number }, string | undefined>({
query: (board_id) => ({
url: getListImagesUrl({
board_id: board_id ?? 'none',
@@ -85,11 +85,11 @@ export const boardsApi = api.injectEndpoints({
{ type: 'BoardImagesTotal', id: arg ?? 'none' },
],
transformResponse: (response: OffsetPaginatedResults_ImageDTO_) => {
- return response.total;
+ return { total: response.total };
},
}),
- getBoardAssetsTotal: build.query({
+ getBoardAssetsTotal: build.query<{ total: number }, string | undefined>({
query: (board_id) => ({
url: getListImagesUrl({
board_id: board_id ?? 'none',
@@ -104,7 +104,7 @@ export const boardsApi = api.injectEndpoints({
{ type: 'BoardAssetsTotal', id: arg ?? 'none' },
],
transformResponse: (response: OffsetPaginatedResults_ImageDTO_) => {
- return response.total;
+ return { total: response.total };
},
}),
diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts
index 239dfaa982..ce9207144d 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/images.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts
@@ -103,6 +103,9 @@ export const imagesApi = api.injectEndpoints({
query: () => ({ url: getListImagesUrl({ is_intermediate: true }) }),
providesTags: ['IntermediatesCount'],
transformResponse: (response: OffsetPaginatedResults_ImageDTO_) => {
+ // TODO: This is storing a primitive value in the cache. `immer` cannot track state changes, so
+ // attempts to use manual cache updates on this value will fail. This should be changed into an
+ // object.
return response.total;
},
}),
@@ -191,35 +194,51 @@ export const imagesApi = api.injectEndpoints({
url: `images/i/${image_name}`,
method: 'DELETE',
}),
- invalidatesTags: (result, error, { board_id }) => [
- { type: 'BoardImagesTotal', id: board_id ?? 'none' },
- { type: 'BoardAssetsTotal', id: board_id ?? 'none' },
- ],
async onQueryStarted(imageDTO, { dispatch, queryFulfilled }) {
/**
* Cache changes for `deleteImage`:
* - NOT POSSIBLE: *remove* from getImageDTO
* - $cache = [board_id|no_board]/[images|assets]
* - *remove* from $cache
+ * - decrement the image's board's total
*/
const { image_name, board_id } = imageDTO;
+ const isAsset = ASSETS_CATEGORIES.includes(imageDTO.image_category);
const queryArg = {
board_id: board_id ?? 'none',
categories: getCategories(imageDTO),
};
- const patch = dispatch(
- imagesApi.util.updateQueryData('listImages', queryArg, (draft) => {
- imagesAdapter.removeOne(draft, image_name);
- })
+ const patches: PatchCollection[] = [];
+
+ patches.push(
+ dispatch(
+ imagesApi.util.updateQueryData('listImages', queryArg, (draft) => {
+ imagesAdapter.removeOne(draft, image_name);
+ })
+ )
);
+ patches.push(
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total = Math.max(draft.total - 1, 0);
+ }
+ )
+ )
+ ); // decrement the image board's total
+
try {
await queryFulfilled;
} catch {
- patch.undo();
+ patches.forEach((patch) => {
+ patch.undo();
+ });
}
},
}),
@@ -237,18 +256,11 @@ export const imagesApi = api.injectEndpoints({
},
};
},
- invalidatesTags: (result, error, { imageDTOs }) => {
- // for now, assume bulk delete is all on one board
- const boardId = imageDTOs[0]?.board_id;
- return [
- { type: 'BoardImagesTotal', id: boardId ?? 'none' },
- { type: 'BoardAssetsTotal', id: boardId ?? 'none' },
- ];
- },
async onQueryStarted({ imageDTOs }, { dispatch, queryFulfilled }) {
/**
* Cache changes for `deleteImages`:
* - *remove* the deleted images from their boards
+ * - decrement the images' board's totals
*
* Unfortunately we cannot do an optimistic update here due to how immer handles patching
* arrays. You have to undo *all* patches, else the entity adapter's `ids` array is borked.
@@ -279,6 +291,21 @@ export const imagesApi = api.injectEndpoints({
}
)
);
+
+ const isAsset = ASSETS_CATEGORIES.includes(
+ imageDTO.image_category
+ );
+
+ // decrement the image board's total
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total = Math.max(draft.total - 1, 0);
+ }
+ )
+ );
}
});
} catch {
@@ -298,10 +325,6 @@ export const imagesApi = api.injectEndpoints({
method: 'PATCH',
body: { is_intermediate },
}),
- invalidatesTags: (result, error, { imageDTO }) => [
- { type: 'BoardImagesTotal', id: imageDTO.board_id ?? 'none' },
- { type: 'BoardAssetsTotal', id: imageDTO.board_id ?? 'none' },
- ],
async onQueryStarted(
{ imageDTO, is_intermediate },
{ dispatch, queryFulfilled, getState }
@@ -312,9 +335,11 @@ export const imagesApi = api.injectEndpoints({
* - $cache = [board_id|no_board]/[images|assets]
* - IF it is being changed to an intermediate:
* - remove from $cache
+ * - decrement the image's board's total
* - ELSE (it is being changed to a non-intermediate):
* - IF it eligible for insertion into existing $cache:
* - *upsert* to $cache
+ * - increment the image's board's total
*/
// Store patches so we can undo if the query fails
@@ -335,6 +360,7 @@ export const imagesApi = api.injectEndpoints({
// $cache = [board_id|no_board]/[images|assets]
const categories = getCategories(imageDTO);
+ const isAsset = ASSETS_CATEGORIES.includes(imageDTO.image_category);
if (is_intermediate) {
// IF it is being changed to an intermediate:
@@ -350,8 +376,35 @@ export const imagesApi = api.injectEndpoints({
)
)
);
+
+ // decrement the image board's total
+ patches.push(
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total = Math.max(draft.total - 1, 0);
+ }
+ )
+ )
+ );
} else {
// ELSE (it is being changed to a non-intermediate):
+
+ // increment the image board's total
+ patches.push(
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total += 1;
+ }
+ )
+ )
+ );
+
const queryArgs = {
board_id: imageDTO.board_id ?? 'none',
categories,
@@ -361,9 +414,7 @@ export const imagesApi = api.injectEndpoints({
getState()
);
- const { data: total } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
+ const { data } = IMAGE_CATEGORIES.includes(imageDTO.image_category)
? boardsApi.endpoints.getBoardImagesTotal.select(
imageDTO.board_id ?? 'none'
)(getState())
@@ -378,7 +429,8 @@ export const imagesApi = api.injectEndpoints({
// - The image's `created_at` is within the range of the cached images
const isCacheFullyPopulated =
- currentCache.data && currentCache.data.ids.length >= (total ?? 0);
+ currentCache.data &&
+ currentCache.data.ids.length >= (data?.total ?? 0);
const isInDateRange = getIsImageInDateRange(
currentCache.data,
@@ -420,10 +472,6 @@ export const imagesApi = api.injectEndpoints({
method: 'PATCH',
body: { session_id },
}),
- invalidatesTags: (result, error, { imageDTO }) => [
- { type: 'BoardImagesTotal', id: imageDTO.board_id ?? 'none' },
- { type: 'BoardAssetsTotal', id: imageDTO.board_id ?? 'none' },
- ],
async onQueryStarted(
{ imageDTO, session_id },
{ dispatch, queryFulfilled }
@@ -535,9 +583,7 @@ export const imagesApi = api.injectEndpoints({
queryArgs
)(getState());
- const { data: previousTotal } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
+ const { data } = IMAGE_CATEGORIES.includes(imageDTO.image_category)
? boardsApi.endpoints.getBoardImagesTotal.select(
boardId ?? 'none'
)(getState())
@@ -547,10 +593,10 @@ export const imagesApi = api.injectEndpoints({
const isCacheFullyPopulated =
currentCache.data &&
- currentCache.data.ids.length >= (previousTotal ?? 0);
+ currentCache.data.ids.length >= (data?.total ?? 0);
const isInDateRange =
- (previousTotal || 0) >= IMAGE_LIMIT
+ (data?.total ?? 0) >= IMAGE_LIMIT
? getIsImageInDateRange(currentCache.data, imageDTO)
: true;
@@ -652,9 +698,7 @@ export const imagesApi = api.injectEndpoints({
queryArgs
)(getState());
- const { data: previousTotal } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
+ const { data } = IMAGE_CATEGORIES.includes(imageDTO.image_category)
? boardsApi.endpoints.getBoardImagesTotal.select(
boardId ?? 'none'
)(getState())
@@ -664,10 +708,10 @@ export const imagesApi = api.injectEndpoints({
const isCacheFullyPopulated =
currentCache.data &&
- currentCache.data.ids.length >= (previousTotal ?? 0);
+ currentCache.data.ids.length >= (data?.total ?? 0);
const isInDateRange =
- (previousTotal || 0) >= IMAGE_LIMIT
+ (data?.total ?? 0) >= IMAGE_LIMIT
? getIsImageInDateRange(currentCache.data, imageDTO)
: true;
@@ -736,6 +780,7 @@ export const imagesApi = api.injectEndpoints({
* - BAIL OUT
* - *add* to `getImageDTO`
* - *add* to no_board/assets
+ * - update the image's board's assets total
*/
const { data: imageDTO } = await queryFulfilled;
@@ -770,11 +815,15 @@ export const imagesApi = api.injectEndpoints({
)
);
+ // increment new board's total
dispatch(
- imagesApi.util.invalidateTags([
- { type: 'BoardImagesTotal', id: imageDTO.board_id ?? 'none' },
- { type: 'BoardAssetsTotal', id: imageDTO.board_id ?? 'none' },
- ])
+ boardsApi.util.updateQueryData(
+ 'getBoardAssetsTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total += 1;
+ }
+ )
);
} catch {
// query failed, no action needed
@@ -801,8 +850,6 @@ export const imagesApi = api.injectEndpoints({
categories: ASSETS_CATEGORIES,
}),
},
- { type: 'BoardImagesTotal', id: 'none' },
- { type: 'BoardAssetsTotal', id: 'none' },
],
async onQueryStarted(board_id, { dispatch, queryFulfilled }) {
/**
@@ -815,6 +862,7 @@ export const imagesApi = api.injectEndpoints({
* have access to the deleted images DTOs - only the names, and a network request
* for all of a board's DTOs could be very large. Instead, we invalidate the 'No Board'
* cache.
+ * - set the board's totals to zero
*/
try {
@@ -834,6 +882,28 @@ export const imagesApi = api.injectEndpoints({
);
});
+ // set the board's asset total to 0 (feels unnecessary since we are deleting it?)
+ dispatch(
+ boardsApi.util.updateQueryData(
+ 'getBoardAssetsTotal',
+ board_id,
+ (draft) => {
+ draft.total = 0;
+ }
+ )
+ );
+
+ // set the board's images total to 0 (feels unnecessary since we are deleting it?)
+ dispatch(
+ boardsApi.util.updateQueryData(
+ 'getBoardImagesTotal',
+ board_id,
+ (draft) => {
+ draft.total = 0;
+ }
+ )
+ );
+
// update 'All Images' & 'All Assets' caches
const queryArgsToUpdate = [
{
@@ -890,8 +960,6 @@ export const imagesApi = api.injectEndpoints({
categories: ASSETS_CATEGORIES,
}),
},
- { type: 'BoardImagesTotal', id: 'none' },
- { type: 'BoardAssetsTotal', id: 'none' },
],
async onQueryStarted(board_id, { dispatch, queryFulfilled }) {
/**
@@ -901,6 +969,7 @@ export const imagesApi = api.injectEndpoints({
* Instead, we rely on the UI to remove all components that use the deleted images.
* - Remove every image in the 'All Images' cache that has the board_id
* - Remove every image in the 'All Assets' cache that has the board_id
+ * - set the board's totals to zero
*/
try {
@@ -928,6 +997,28 @@ export const imagesApi = api.injectEndpoints({
)
);
});
+
+ // set the board's asset total to 0 (feels unnecessary since we are deleting it?)
+ dispatch(
+ boardsApi.util.updateQueryData(
+ 'getBoardAssetsTotal',
+ board_id,
+ (draft) => {
+ draft.total = 0;
+ }
+ )
+ );
+
+ // set the board's images total to 0 (feels unnecessary since we are deleting it?)
+ dispatch(
+ boardsApi.util.updateQueryData(
+ 'getBoardImagesTotal',
+ board_id,
+ (draft) => {
+ draft.total = 0;
+ }
+ )
+ );
} catch {
//no-op
}
@@ -945,15 +1036,9 @@ export const imagesApi = api.injectEndpoints({
body: { board_id, image_name },
};
},
- invalidatesTags: (result, error, { board_id, imageDTO }) => [
+ invalidatesTags: (result, error, { board_id }) => [
// refresh the board itself
{ type: 'Board', id: board_id },
- // update old board totals
- { type: 'BoardImagesTotal', id: board_id },
- { type: 'BoardAssetsTotal', id: board_id },
- // update new board totals
- { type: 'BoardImagesTotal', id: imageDTO.board_id ?? 'none' },
- { type: 'BoardAssetsTotal', id: imageDTO.board_id ?? 'none' },
],
async onQueryStarted(
{ board_id, imageDTO },
@@ -970,11 +1055,13 @@ export const imagesApi = api.injectEndpoints({
* - $cache = board_id/[images|assets]
* - IF it eligible for insertion into existing $cache:
* - THEN *add* to $cache
+ * - decrement both old board's total
+ * - increment the new board's total
*/
const patches: PatchCollection[] = [];
const categories = getCategories(imageDTO);
-
+ const isAsset = ASSETS_CATEGORIES.includes(imageDTO.image_category);
// *update* getImageDTO
patches.push(
dispatch(
@@ -1005,6 +1092,32 @@ export const imagesApi = api.injectEndpoints({
)
);
+ // decrement old board's total
+ patches.push(
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total = Math.max(draft.total - 1, 0);
+ }
+ )
+ )
+ );
+
+ // increment new board's total
+ patches.push(
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ board_id ?? 'none',
+ (draft) => {
+ draft.total += 1;
+ }
+ )
+ )
+ );
+
// $cache = board_id/[images|assets]
const queryArgs = { board_id: board_id ?? 'none', categories };
const currentCache = imagesApi.endpoints.listImages.select(queryArgs)(
@@ -1017,9 +1130,7 @@ export const imagesApi = api.injectEndpoints({
// OR
// - The image's `created_at` is within the range of the cached images
- const { data: total } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
+ const { data } = IMAGE_CATEGORIES.includes(imageDTO.image_category)
? boardsApi.endpoints.getBoardImagesTotal.select(
imageDTO.board_id ?? 'none'
)(getState())
@@ -1028,7 +1139,8 @@ export const imagesApi = api.injectEndpoints({
)(getState());
const isCacheFullyPopulated =
- currentCache.data && currentCache.data.ids.length >= (total ?? 0);
+ currentCache.data &&
+ currentCache.data.ids.length >= (data?.total ?? 0);
const isInDateRange = getIsImageInDateRange(
currentCache.data,
@@ -1072,12 +1184,6 @@ export const imagesApi = api.injectEndpoints({
return [
// invalidate the image's old board
{ type: 'Board', id: board_id ?? 'none' },
- // update old board totals
- { type: 'BoardImagesTotal', id: board_id ?? 'none' },
- { type: 'BoardAssetsTotal', id: board_id ?? 'none' },
- // update the no_board totals
- { type: 'BoardImagesTotal', id: 'none' },
- { type: 'BoardAssetsTotal', id: 'none' },
];
},
async onQueryStarted(
@@ -1091,10 +1197,13 @@ export const imagesApi = api.injectEndpoints({
* - $cache = no_board/[images|assets]
* - IF it eligible for insertion into existing $cache:
* - THEN *upsert* to $cache
+ * - decrement old board's total
+ * - increment the new board's total (no board)
*/
const categories = getCategories(imageDTO);
const patches: PatchCollection[] = [];
+ const isAsset = ASSETS_CATEGORIES.includes(imageDTO.image_category);
// *update* getImageDTO
patches.push(
@@ -1125,6 +1234,32 @@ export const imagesApi = api.injectEndpoints({
)
);
+ // decrement old board's total
+ patches.push(
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total = Math.max(draft.total - 1, 0);
+ }
+ )
+ )
+ );
+
+ // increment new board's total (no board)
+ patches.push(
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ 'none',
+ (draft) => {
+ draft.total += 1;
+ }
+ )
+ )
+ );
+
// $cache = no_board/[images|assets]
const queryArgs = { board_id: 'none', categories };
const currentCache = imagesApi.endpoints.listImages.select(queryArgs)(
@@ -1137,9 +1272,7 @@ export const imagesApi = api.injectEndpoints({
// OR
// - The image's `created_at` is within the range of the cached images
- const { data: total } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
+ const { data } = IMAGE_CATEGORIES.includes(imageDTO.image_category)
? boardsApi.endpoints.getBoardImagesTotal.select(
imageDTO.board_id ?? 'none'
)(getState())
@@ -1148,7 +1281,8 @@ export const imagesApi = api.injectEndpoints({
)(getState());
const isCacheFullyPopulated =
- currentCache.data && currentCache.data.ids.length >= (total ?? 0);
+ currentCache.data &&
+ currentCache.data.ids.length >= (data?.total ?? 0);
const isInDateRange = getIsImageInDateRange(
currentCache.data,
@@ -1192,21 +1326,10 @@ export const imagesApi = api.injectEndpoints({
board_id,
},
}),
- invalidatesTags: (result, error, { imageDTOs, board_id }) => {
- //assume all images are being moved from one board for now
- const oldBoardId = imageDTOs[0]?.board_id;
+ invalidatesTags: (result, error, { board_id }) => {
return [
// update the destination board
{ type: 'Board', id: board_id ?? 'none' },
- // update new board totals
- { type: 'BoardImagesTotal', id: board_id ?? 'none' },
- { type: 'BoardAssetsTotal', id: board_id ?? 'none' },
- // update old board totals
- { type: 'BoardImagesTotal', id: oldBoardId ?? 'none' },
- { type: 'BoardAssetsTotal', id: oldBoardId ?? 'none' },
- // update the no_board totals
- { type: 'BoardImagesTotal', id: 'none' },
- { type: 'BoardAssetsTotal', id: 'none' },
];
},
async onQueryStarted(
@@ -1222,6 +1345,8 @@ export const imagesApi = api.injectEndpoints({
* - *update* getImageDTO for each image
* - *add* to board_id/[images|assets]
* - *remove* from [old_board_id|no_board]/[images|assets]
+ * - decrement old board's totals for each image
+ * - increment new board's totals for each image
*/
added_image_names.forEach((image_name) => {
@@ -1230,7 +1355,8 @@ export const imagesApi = api.injectEndpoints({
'getImageDTO',
image_name,
(draft) => {
- draft.board_id = new_board_id;
+ draft.board_id =
+ new_board_id === 'none' ? undefined : new_board_id;
}
)
);
@@ -1243,6 +1369,7 @@ export const imagesApi = api.injectEndpoints({
const categories = getCategories(imageDTO);
const old_board_id = imageDTO.board_id;
+ const isAsset = ASSETS_CATEGORIES.includes(imageDTO.image_category);
// remove from the old board
dispatch(
@@ -1255,6 +1382,28 @@ export const imagesApi = api.injectEndpoints({
)
);
+ // decrement old board's total
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ old_board_id ?? 'none',
+ (draft) => {
+ draft.total = Math.max(draft.total - 1, 0);
+ }
+ )
+ );
+
+ // increment new board's total
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ new_board_id ?? 'none',
+ (draft) => {
+ draft.total += 1;
+ }
+ )
+ );
+
const queryArgs = {
board_id: new_board_id,
categories,
@@ -1264,9 +1413,7 @@ export const imagesApi = api.injectEndpoints({
queryArgs
)(getState());
- const { data: previousTotal } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
+ const { data } = IMAGE_CATEGORIES.includes(imageDTO.image_category)
? boardsApi.endpoints.getBoardImagesTotal.select(
new_board_id ?? 'none'
)(getState())
@@ -1276,10 +1423,10 @@ export const imagesApi = api.injectEndpoints({
const isCacheFullyPopulated =
currentCache.data &&
- currentCache.data.ids.length >= (previousTotal ?? 0);
+ currentCache.data.ids.length >= (data?.total ?? 0);
const isInDateRange =
- (previousTotal || 0) >= IMAGE_LIMIT
+ (data?.total ?? 0) >= IMAGE_LIMIT
? getIsImageInDateRange(currentCache.data, imageDTO)
: true;
@@ -1319,10 +1466,7 @@ export const imagesApi = api.injectEndpoints({
}),
invalidatesTags: (result, error, { imageDTOs }) => {
const touchedBoardIds: string[] = [];
- const tags: ApiTagDescription[] = [
- { type: 'BoardImagesTotal', id: 'none' },
- { type: 'BoardAssetsTotal', id: 'none' },
- ];
+ const tags: ApiTagDescription[] = [];
result?.removed_image_names.forEach((image_name) => {
const board_id = imageDTOs.find((i) => i.image_name === image_name)
@@ -1333,8 +1477,6 @@ export const imagesApi = api.injectEndpoints({
}
tags.push({ type: 'Board', id: board_id });
- tags.push({ type: 'BoardImagesTotal', id: board_id });
- tags.push({ type: 'BoardAssetsTotal', id: board_id });
});
return tags;
@@ -1352,6 +1494,8 @@ export const imagesApi = api.injectEndpoints({
* - *update* getImageDTO for each image
* - *remove* from old_board_id/[images|assets]
* - *add* to no_board/[images|assets]
+ * - decrement old board's totals for each image
+ * - increment new board's (no board) totals for each image
*/
removed_image_names.forEach((image_name) => {
@@ -1372,6 +1516,7 @@ export const imagesApi = api.injectEndpoints({
}
const categories = getCategories(imageDTO);
+ const isAsset = ASSETS_CATEGORIES.includes(imageDTO.image_category);
// remove from the old board
dispatch(
@@ -1384,6 +1529,28 @@ export const imagesApi = api.injectEndpoints({
)
);
+ // decrement old board's total
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ imageDTO.board_id ?? 'none',
+ (draft) => {
+ draft.total = Math.max(draft.total - 1, 0);
+ }
+ )
+ );
+
+ // increment new board's total (no board)
+ dispatch(
+ boardsApi.util.updateQueryData(
+ isAsset ? 'getBoardAssetsTotal' : 'getBoardImagesTotal',
+ 'none',
+ (draft) => {
+ draft.total += 1;
+ }
+ )
+ );
+
// add to `no_board`
const queryArgs = {
board_id: 'none',
@@ -1394,9 +1561,7 @@ export const imagesApi = api.injectEndpoints({
queryArgs
)(getState());
- const { data: total } = IMAGE_CATEGORIES.includes(
- imageDTO.image_category
- )
+ const { data } = IMAGE_CATEGORIES.includes(imageDTO.image_category)
? boardsApi.endpoints.getBoardImagesTotal.select(
imageDTO.board_id ?? 'none'
)(getState())
@@ -1405,10 +1570,11 @@ export const imagesApi = api.injectEndpoints({
)(getState());
const isCacheFullyPopulated =
- currentCache.data && currentCache.data.ids.length >= (total ?? 0);
+ currentCache.data &&
+ currentCache.data.ids.length >= (data?.total ?? 0);
const isInDateRange =
- (total || 0) >= IMAGE_LIMIT
+ (data?.total ?? 0) >= IMAGE_LIMIT
? getIsImageInDateRange(currentCache.data, imageDTO)
: true;
diff --git a/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts b/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts
index a350979b89..29162597c5 100644
--- a/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts
+++ b/invokeai/frontend/web/src/services/api/hooks/useBoardTotal.ts
@@ -13,7 +13,7 @@ export const useBoardTotal = (board_id: BoardId) => {
const { data: totalAssets } = useGetBoardAssetsTotalQuery(board_id);
const currentViewTotal = useMemo(
- () => (galleryView === 'images' ? totalImages : totalAssets),
+ () => (galleryView === 'images' ? totalImages?.total : totalAssets?.total),
[galleryView, totalAssets, totalImages]
);