ui, db: rand fixes (#3715)

[feat(ui): memoize ImageContextMenu
selector](265996d230)

Without the selector itself being memoized, the gallery was rerendering
on every progress image.

[feat(ui): memoize NextPrevImageButtons
component](a7b8109ac2)

This was rerendering on every progress image, now it doesn't

[fix(ui): correctly set disabled on invoke button during
generation](1c45d18e6d)

It wasn't disabled when it should have been, looked clickable during
generation.

[fix(nodes): remove board_id column from images
table](00e26ffa9a)

This is extraneous; the `board_images` table holds image-board
relationships. @maryhipp
This commit is contained in:
blessedcoolant 2023-07-10 17:10:28 +12:00 committed by GitHub
commit 8b4e153acc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 55 deletions

View File

@ -1,22 +1,16 @@
import sqlite3
import threading
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from datetime import datetime from datetime import datetime
from typing import Generic, Optional, TypeVar, cast from typing import Generic, Optional, TypeVar, cast
import sqlite3
import threading
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from pydantic.generics import GenericModel from pydantic.generics import GenericModel
from invokeai.app.models.image import ImageCategory, ResourceOrigin
from invokeai.app.models.metadata import ImageMetadata from invokeai.app.models.metadata import ImageMetadata
from invokeai.app.models.image import (
ImageCategory,
ResourceOrigin,
)
from invokeai.app.services.models.image_record import ( from invokeai.app.services.models.image_record import (
ImageRecord, ImageRecord, ImageRecordChanges, deserialize_image_record)
ImageRecordChanges,
deserialize_image_record,
)
T = TypeVar("T", bound=BaseModel) T = TypeVar("T", bound=BaseModel)
@ -162,7 +156,6 @@ class SqliteImageRecordStorage(ImageRecordStorageBase):
node_id TEXT, node_id TEXT,
metadata TEXT, metadata TEXT,
is_intermediate BOOLEAN DEFAULT FALSE, is_intermediate BOOLEAN DEFAULT FALSE,
board_id TEXT,
created_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), created_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),
-- Updated via trigger -- Updated via trigger
updated_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), updated_at DATETIME NOT NULL DEFAULT(STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')),

View File

@ -1,49 +1,32 @@
import { MenuItem, MenuList } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { memo, useCallback, useContext } from 'react';
import {
FaExpand,
FaFolder,
FaFolderPlus,
FaShare,
FaTrash,
} from 'react-icons/fa';
import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu';
import {
resizeAndScaleCanvas,
setInitialCanvasImage,
} from 'features/canvas/store/canvasSlice';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { useTranslation } from 'react-i18next';
import { ExternalLinkIcon } from '@chakra-ui/icons'; import { ExternalLinkIcon } from '@chakra-ui/icons';
import { IoArrowUndoCircleOutline } from 'react-icons/io5'; import { MenuItem, MenuList } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
import { initialImageSelected } from 'features/parameters/store/actions';
import { sentImageToCanvas, sentImageToImg2Img } from '../store/actions';
import { useAppToaster } from 'app/components/Toaster'; import { useAppToaster } from 'app/components/Toaster';
import { AddImageToBoardContext } from '../../../app/contexts/AddImageToBoardContext'; import { stateSelector } from 'app/store/store';
import { useRemoveImageFromBoardMutation } from 'services/api/endpoints/boardImages'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { ImageDTO } from 'services/api/types'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { RootState, stateSelector } from 'app/store/store'; import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu';
import { import {
imagesAddedToBatch, imagesAddedToBatch,
selectionAddedToBatch, selectionAddedToBatch,
} from 'features/batch/store/batchSlice'; } from 'features/batch/store/batchSlice';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import {
resizeAndScaleCanvas,
setInitialCanvasImage,
} from 'features/canvas/store/canvasSlice';
import { imageToDeleteSelected } from 'features/imageDeletion/store/imageDeletionSlice'; import { imageToDeleteSelected } from 'features/imageDeletion/store/imageDeletionSlice';
import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters';
const selector = createSelector( import { initialImageSelected } from 'features/parameters/store/actions';
[stateSelector, (state: RootState, imageDTO: ImageDTO) => imageDTO], import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
({ gallery, batch }, imageDTO) => { import { setActiveTab } from 'features/ui/store/uiSlice';
const selectionCount = gallery.selection.length; import { memo, useCallback, useContext, useMemo } from 'react';
const isInBatch = batch.imageNames.includes(imageDTO.image_name); import { useTranslation } from 'react-i18next';
import { FaExpand, FaFolder, FaShare, FaTrash } from 'react-icons/fa';
return { selectionCount, isInBatch }; import { IoArrowUndoCircleOutline } from 'react-icons/io5';
}, import { useRemoveImageFromBoardMutation } from 'services/api/endpoints/boardImages';
defaultSelectorOptions import { ImageDTO } from 'services/api/types';
); import { AddImageToBoardContext } from '../../../app/contexts/AddImageToBoardContext';
import { sentImageToCanvas, sentImageToImg2Img } from '../store/actions';
type Props = { type Props = {
image: ImageDTO; image: ImageDTO;
@ -51,9 +34,21 @@ type Props = {
}; };
const ImageContextMenu = ({ image, children }: Props) => { const ImageContextMenu = ({ image, children }: Props) => {
const { selectionCount, isInBatch } = useAppSelector((state) => const selector = useMemo(
selector(state, image) () =>
createSelector(
[stateSelector],
({ gallery, batch }) => {
const selectionCount = gallery.selection.length;
const isInBatch = batch.imageNames.includes(image.image_name);
return { selectionCount, isInBatch };
},
defaultSelectorOptions
),
[image.image_name]
); );
const { selectionCount, isInBatch } = useAppSelector(selector);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -8,7 +8,7 @@ import {
selectImagesById, selectImagesById,
} from 'features/gallery/store/gallerySlice'; } from 'features/gallery/store/gallerySlice';
import { clamp, isEqual } from 'lodash-es'; import { clamp, isEqual } from 'lodash-es';
import { useCallback, useState } from 'react'; import { memo, useCallback, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { FaAngleDoubleRight, FaAngleLeft, FaAngleRight } from 'react-icons/fa'; import { FaAngleDoubleRight, FaAngleLeft, FaAngleRight } from 'react-icons/fa';
@ -227,4 +227,4 @@ const NextPrevImageButtons = () => {
); );
}; };
export default NextPrevImageButtons; export default memo(NextPrevImageButtons);

View File

@ -78,7 +78,7 @@ export default function InvokeButton(props: InvokeButton) {
aria-label={t('parameters.invoke')} aria-label={t('parameters.invoke')}
type="submit" type="submit"
icon={<FaPlay />} icon={<FaPlay />}
isDisabled={!isReady} isDisabled={!isReady || isProcessing}
onClick={handleInvoke} onClick={handleInvoke}
tooltip={t('parameters.invoke')} tooltip={t('parameters.invoke')}
tooltipProps={{ placement: 'top' }} tooltipProps={{ placement: 'top' }}
@ -95,7 +95,7 @@ export default function InvokeButton(props: InvokeButton) {
<IAIButton <IAIButton
aria-label={t('parameters.invoke')} aria-label={t('parameters.invoke')}
type="submit" type="submit"
isDisabled={!isReady} isDisabled={!isReady || isProcessing}
onClick={handleInvoke} onClick={handleInvoke}
colorScheme="accent" colorScheme="accent"
id="invoke-button" id="invoke-button"