Fixes current image button rerenders

This commit is contained in:
psychedelicious 2022-11-02 21:28:22 +11:00 committed by Lincoln Stein
parent ccf8593501
commit 300bb2e627
4 changed files with 86 additions and 74 deletions

View File

@ -1,8 +1,6 @@
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import * as InvokeAI from '../../app/invokeai';
import { useAppDispatch, useAppSelector } from '../../app/store'; import { useAppDispatch, useAppSelector } from '../../app/store';
import { RootState } from '../../app/store'; import { RootState } from '../../app/store';
import { import {
@ -60,7 +58,7 @@ const systemSelector = createSelector(
const { upscalingLevel, facetoolStrength, shouldShowImageDetails } = const { upscalingLevel, facetoolStrength, shouldShowImageDetails } =
options; options;
const { intermediateImage } = gallery; const { intermediateImage, currentImage } = gallery;
return { return {
isProcessing, isProcessing,
@ -69,7 +67,8 @@ const systemSelector = createSelector(
isESRGANAvailable, isESRGANAvailable,
upscalingLevel, upscalingLevel,
facetoolStrength, facetoolStrength,
intermediateImage, shouldDisableToolbarButtons: Boolean(intermediateImage) || !currentImage,
currentImage,
shouldShowImageDetails, shouldShowImageDetails,
activeTabName, activeTabName,
}; };
@ -81,15 +80,11 @@ const systemSelector = createSelector(
} }
); );
type CurrentImageButtonsProps = {
image: InvokeAI.Image;
};
/** /**
* Row of buttons for common actions: * Row of buttons for common actions:
* Use as init image, use all params, use seed, upscale, fix faces, details, delete. * Use as init image, use all params, use seed, upscale, fix faces, details, delete.
*/ */
const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => { const CurrentImageButtons = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { const {
isProcessing, isProcessing,
@ -98,17 +93,21 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
isESRGANAvailable, isESRGANAvailable,
upscalingLevel, upscalingLevel,
facetoolStrength, facetoolStrength,
intermediateImage, shouldDisableToolbarButtons,
shouldShowImageDetails, shouldShowImageDetails,
activeTabName, activeTabName,
currentImage,
} = useAppSelector(systemSelector); } = useAppSelector(systemSelector);
const { onCopy } = useClipboard(window.location.toString() + image.url); const { onCopy } = useClipboard(
currentImage ? window.location.toString() + currentImage.url : ''
);
const toast = useToast(); const toast = useToast();
const handleClickUseAsInitialImage = () => { const handleClickUseAsInitialImage = () => {
dispatch(setInitialImage(image)); if (!currentImage) return;
dispatch(setInitialImage(currentImage));
dispatch(setActiveTab(1)); dispatch(setActiveTab(1));
}; };
@ -125,7 +124,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
useHotkeys( useHotkeys(
'shift+i', 'shift+i',
() => { () => {
if (image) { if (currentImage) {
handleClickUseAsInitialImage(); handleClickUseAsInitialImage();
toast({ toast({
title: 'Sent To Image To Image', title: 'Sent To Image To Image',
@ -143,16 +142,20 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
}); });
} }
}, },
[image] [currentImage]
); );
const handleClickUseAllParameters = () => const handleClickUseAllParameters = () => {
image.metadata && dispatch(setAllParameters(image.metadata)); if (!currentImage) return;
currentImage.metadata && dispatch(setAllParameters(currentImage.metadata));
};
useHotkeys( useHotkeys(
'a', 'a',
() => { () => {
if (['txt2img', 'img2img'].includes(image?.metadata?.image?.type)) { if (
['txt2img', 'img2img'].includes(currentImage?.metadata?.image?.type)
) {
handleClickUseAllParameters(); handleClickUseAllParameters();
toast({ toast({
title: 'Parameters Set', title: 'Parameters Set',
@ -170,15 +173,18 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
}); });
} }
}, },
[image] [currentImage]
); );
const handleClickUseSeed = () => const handleClickUseSeed = () => {
image.metadata && dispatch(setSeed(image.metadata.image.seed)); currentImage?.metadata &&
dispatch(setSeed(currentImage.metadata.image.seed));
};
useHotkeys( useHotkeys(
's', 's',
() => { () => {
if (image?.metadata?.image?.seed) { if (currentImage?.metadata?.image?.seed) {
handleClickUseSeed(); handleClickUseSeed();
toast({ toast({
title: 'Seed Set', title: 'Seed Set',
@ -196,17 +202,17 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
}); });
} }
}, },
[image] [currentImage]
); );
const handleClickUsePrompt = () => const handleClickUsePrompt = () =>
image?.metadata?.image?.prompt && currentImage?.metadata?.image?.prompt &&
dispatch(setPrompt(image.metadata.image.prompt)); dispatch(setPrompt(currentImage.metadata.image.prompt));
useHotkeys( useHotkeys(
'p', 'p',
() => { () => {
if (image?.metadata?.image?.prompt) { if (currentImage?.metadata?.image?.prompt) {
handleClickUsePrompt(); handleClickUsePrompt();
toast({ toast({
title: 'Prompt Set', title: 'Prompt Set',
@ -224,16 +230,19 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
}); });
} }
}, },
[image] [currentImage]
); );
const handleClickUpscale = () => dispatch(runESRGAN(image)); const handleClickUpscale = () => {
currentImage && dispatch(runESRGAN(currentImage));
};
useHotkeys( useHotkeys(
'u', 'u',
() => { () => {
if ( if (
isESRGANAvailable && isESRGANAvailable &&
Boolean(!intermediateImage) && !shouldDisableToolbarButtons &&
isConnected && isConnected &&
!isProcessing && !isProcessing &&
upscalingLevel upscalingLevel
@ -249,23 +258,25 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
} }
}, },
[ [
image, currentImage,
isESRGANAvailable, isESRGANAvailable,
intermediateImage, shouldDisableToolbarButtons,
isConnected, isConnected,
isProcessing, isProcessing,
upscalingLevel, upscalingLevel,
] ]
); );
const handleClickFixFaces = () => dispatch(runFacetool(image)); const handleClickFixFaces = () => {
currentImage && dispatch(runFacetool(currentImage));
};
useHotkeys( useHotkeys(
'r', 'r',
() => { () => {
if ( if (
isGFPGANAvailable && isGFPGANAvailable &&
Boolean(!intermediateImage) && !shouldDisableToolbarButtons &&
isConnected && isConnected &&
!isProcessing && !isProcessing &&
facetoolStrength facetoolStrength
@ -281,9 +292,9 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
} }
}, },
[ [
image, currentImage,
isGFPGANAvailable, isGFPGANAvailable,
intermediateImage, shouldDisableToolbarButtons,
isConnected, isConnected,
isProcessing, isProcessing,
facetoolStrength, facetoolStrength,
@ -294,7 +305,9 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
dispatch(setShouldShowImageDetails(!shouldShowImageDetails)); dispatch(setShouldShowImageDetails(!shouldShowImageDetails));
const handleSendToInpainting = () => { const handleSendToInpainting = () => {
dispatch(setImageToInpaint(image)); if (!currentImage) return;
dispatch(setImageToInpaint(currentImage));
if (activeTabName !== 'inpainting') { if (activeTabName !== 'inpainting') {
dispatch(setActiveTab('inpainting')); dispatch(setActiveTab('inpainting'));
} }
@ -309,7 +322,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
useHotkeys( useHotkeys(
'i', 'i',
() => { () => {
if (image) { if (currentImage) {
handleClickShowImageDetails(); handleClickShowImageDetails();
} else { } else {
toast({ toast({
@ -320,7 +333,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
}); });
} }
}, },
[image, shouldShowImageDetails] [currentImage, shouldShowImageDetails]
); );
return ( return (
@ -356,7 +369,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
</IAIButton> </IAIButton>
<IAIButton leftIcon={<FaDownload />} size={'sm'}> <IAIButton leftIcon={<FaDownload />} size={'sm'}>
<Link download={true} href={image.url}> <Link download={true} href={currentImage?.url}>
Download Image Download Image
</Link> </Link>
</IAIButton> </IAIButton>
@ -367,7 +380,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
icon={<FaQuoteRight />} icon={<FaQuoteRight />}
tooltip="Use Prompt" tooltip="Use Prompt"
aria-label="Use Prompt" aria-label="Use Prompt"
isDisabled={!image?.metadata?.image?.prompt} isDisabled={!currentImage?.metadata?.image?.prompt}
onClick={handleClickUsePrompt} onClick={handleClickUsePrompt}
/> />
@ -375,7 +388,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
icon={<FaSeedling />} icon={<FaSeedling />}
tooltip="Use Seed" tooltip="Use Seed"
aria-label="Use Seed" aria-label="Use Seed"
isDisabled={!image?.metadata?.image?.seed} isDisabled={!currentImage?.metadata?.image?.seed}
onClick={handleClickUseSeed} onClick={handleClickUseSeed}
/> />
@ -384,7 +397,9 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
tooltip="Use All" tooltip="Use All"
aria-label="Use All" aria-label="Use All"
isDisabled={ isDisabled={
!['txt2img', 'img2img'].includes(image?.metadata?.image?.type) !['txt2img', 'img2img'].includes(
currentImage?.metadata?.image?.type
)
} }
onClick={handleClickUseAllParameters} onClick={handleClickUseAllParameters}
/> />
@ -400,7 +415,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
<IAIButton <IAIButton
isDisabled={ isDisabled={
!isGFPGANAvailable || !isGFPGANAvailable ||
Boolean(intermediateImage) || !currentImage ||
!(isConnected && !isProcessing) || !(isConnected && !isProcessing) ||
!facetoolStrength !facetoolStrength
} }
@ -422,7 +437,7 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
<IAIButton <IAIButton
isDisabled={ isDisabled={
!isESRGANAvailable || !isESRGANAvailable ||
Boolean(intermediateImage) || !currentImage ||
!(isConnected && !isProcessing) || !(isConnected && !isProcessing) ||
!upscalingLevel !upscalingLevel
} }
@ -441,14 +456,12 @@ const CurrentImageButtons = ({ image }: CurrentImageButtonsProps) => {
onClick={handleClickShowImageDetails} onClick={handleClickShowImageDetails}
/> />
<DeleteImageModal image={image}> <DeleteImageModal image={currentImage}>
<IAIIconButton <IAIIconButton
icon={<FaTrash />} icon={<FaTrash />}
tooltip="Delete Image" tooltip="Delete Image"
aria-label="Delete Image" aria-label="Delete Image"
isDisabled={ isDisabled={Boolean(currentImage) || !isConnected || isProcessing}
Boolean(intermediateImage) || !isConnected || isProcessing
}
/> />
</DeleteImageModal> </DeleteImageModal>
</ButtonGroup> </ButtonGroup>

View File

@ -19,10 +19,9 @@ export const currentImageDisplaySelector = createSelector(
const { shouldShowImageDetails } = options; const { shouldShowImageDetails } = options;
return { return {
currentImage,
intermediateImage,
activeTabName, activeTabName,
shouldShowImageDetails, shouldShowImageDetails,
hasAnImageToDisplay: currentImage || intermediateImage,
}; };
}, },
{ {
@ -36,18 +35,16 @@ export const currentImageDisplaySelector = createSelector(
* Displays the current image if there is one, plus associated actions. * Displays the current image if there is one, plus associated actions.
*/ */
const CurrentImageDisplay = () => { const CurrentImageDisplay = () => {
const { currentImage, intermediateImage, activeTabName } = useAppSelector( const { hasAnImageToDisplay, activeTabName } = useAppSelector(
currentImageDisplaySelector currentImageDisplaySelector
); );
const imageToDisplay = intermediateImage || currentImage;
return ( return (
<div className="current-image-area" data-tab-name={activeTabName}> <div className="current-image-area" data-tab-name={activeTabName}>
{imageToDisplay ? ( {hasAnImageToDisplay ? (
<> <>
<CurrentImageButtons image={imageToDisplay} /> <CurrentImageButtons />
<CurrentImagePreview imageToDisplay={imageToDisplay} /> <CurrentImagePreview />
</> </>
) : ( ) : (
<div className="current-image-display-placeholder"> <div className="current-image-display-placeholder">

View File

@ -3,7 +3,6 @@ import { useState } from 'react';
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa'; import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
import { RootState, useAppDispatch, useAppSelector } from '../../app/store'; import { RootState, useAppDispatch, useAppSelector } from '../../app/store';
import { GalleryState, selectNextImage, selectPrevImage } from './gallerySlice'; import { GalleryState, selectNextImage, selectPrevImage } from './gallerySlice';
import * as InvokeAI from '../../app/invokeai';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import _ from 'lodash'; import _ from 'lodash';
import { OptionsState } from '../options/optionsSlice'; import { OptionsState } from '../options/optionsSlice';
@ -12,7 +11,7 @@ import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
export const imagesSelector = createSelector( export const imagesSelector = createSelector(
[(state: RootState) => state.gallery, (state: RootState) => state.options], [(state: RootState) => state.gallery, (state: RootState) => state.options],
(gallery: GalleryState, options: OptionsState) => { (gallery: GalleryState, options: OptionsState) => {
const { currentCategory } = gallery; const { currentCategory, currentImage, intermediateImage } = gallery;
const { shouldShowImageDetails } = options; const { shouldShowImageDetails } = options;
const tempImages = gallery.categories[currentCategory].images; const tempImages = gallery.categories[currentCategory].images;
@ -21,6 +20,7 @@ export const imagesSelector = createSelector(
); );
const imagesLength = tempImages.length; const imagesLength = tempImages.length;
return { return {
imageToDisplay: intermediateImage ? intermediateImage : currentImage,
currentCategory, currentCategory,
isOnFirstImage: currentImageIndex === 0, isOnFirstImage: currentImageIndex === 0,
isOnLastImage: isOnLastImage:
@ -38,16 +38,15 @@ export const imagesSelector = createSelector(
} }
); );
interface CurrentImagePreviewProps { export default function CurrentImagePreview() {
imageToDisplay: InvokeAI.Image;
}
export default function CurrentImagePreview(props: CurrentImagePreviewProps) {
const { imageToDisplay } = props;
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { isOnFirstImage, isOnLastImage, shouldShowImageDetails } = const {
useAppSelector(imagesSelector); isOnFirstImage,
isOnLastImage,
shouldShowImageDetails,
imageToDisplay,
} = useAppSelector(imagesSelector);
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] = const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] =
useState<boolean>(false); useState<boolean>(false);
@ -67,14 +66,17 @@ export default function CurrentImagePreview(props: CurrentImagePreviewProps) {
const handleClickNextButton = () => { const handleClickNextButton = () => {
dispatch(selectNextImage()); dispatch(selectNextImage());
}; };
console.log(imageToDisplay);
return ( return (
<div className={'current-image-preview'}> <div className={'current-image-preview'}>
{imageToDisplay && (
<Image <Image
src={imageToDisplay.url} src={imageToDisplay.url}
width={imageToDisplay.width} width={imageToDisplay.width}
height={imageToDisplay.height} height={imageToDisplay.height}
/> />
)}
{!shouldShowImageDetails && ( {!shouldShowImageDetails && (
<div className="current-image-next-prev-buttons"> <div className="current-image-next-prev-buttons">
<div <div
@ -107,7 +109,7 @@ export default function CurrentImagePreview(props: CurrentImagePreviewProps) {
</div> </div>
</div> </div>
)} )}
{shouldShowImageDetails && ( {shouldShowImageDetails && imageToDisplay && (
<ImageMetadataViewer <ImageMetadataViewer
image={imageToDisplay} image={imageToDisplay}
styleClass="current-image-metadata" styleClass="current-image-metadata"

View File

@ -50,7 +50,7 @@ interface DeleteImageModalProps {
/** /**
* The image to delete. * The image to delete.
*/ */
image: InvokeAI.Image; image?: InvokeAI.Image;
} }
/** /**
@ -73,7 +73,7 @@ const DeleteImageModal = forwardRef(
}; };
const handleDelete = () => { const handleDelete = () => {
if (isConnected && !isProcessing) { if (isConnected && !isProcessing && image) {
dispatch(deleteImage(image)); dispatch(deleteImage(image));
} }
onClose(); onClose();
@ -95,7 +95,7 @@ const DeleteImageModal = forwardRef(
<> <>
{cloneElement(children, { {cloneElement(children, {
// TODO: This feels wrong. // TODO: This feels wrong.
onClick: handleClickDelete, onClick: image ? handleClickDelete : undefined,
ref: ref, ref: ref,
})} })}