refactor(ui): metadata recall buttons & hotkeys (WIP)

This commit is contained in:
psychedelicious
2025-07-09 17:33:07 +10:00
parent 7f2dd22d47
commit 7adc33e04d
13 changed files with 140 additions and 147 deletions

View File

@@ -1,4 +1,5 @@
import { useAppSelector } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useLoadWorkflow } from 'features/gallery/hooks/useLoadWorkflow';
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
@@ -27,6 +28,11 @@ export const GlobalImageHotkeys = memo(() => {
GlobalImageHotkeys.displayName = 'GlobalImageHotkeys';
const GlobalImageHotkeysInternal = memo(({ imageDTO }: { imageDTO: ImageDTO }) => {
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const isFocusOK = isGalleryFocused || isViewerFocused;
const recallAll = useRecallAll(imageDTO);
const recallRemix = useRecallRemix(imageDTO);
const recallPrompts = useRecallPrompts(imageDTO);
@@ -38,48 +44,48 @@ const GlobalImageHotkeysInternal = memo(({ imageDTO }: { imageDTO: ImageDTO }) =
id: 'loadWorkflow',
category: 'viewer',
callback: loadWorkflow.load,
options: { enabled: loadWorkflow.isEnabled },
dependencies: [loadWorkflow],
options: { enabled: loadWorkflow.isEnabled && isFocusOK },
dependencies: [loadWorkflow, isFocusOK],
});
useRegisteredHotkeys({
id: 'recallAll',
category: 'viewer',
callback: recallAll.recall,
options: { enabled: recallAll.isEnabled },
dependencies: [recallAll],
options: { enabled: recallAll.isEnabled && isFocusOK },
dependencies: [recallAll, isFocusOK],
});
useRegisteredHotkeys({
id: 'recallSeed',
category: 'viewer',
callback: recallSeed.recall,
options: { enabled: recallSeed.isEnabled },
dependencies: [recallSeed],
options: { enabled: recallSeed.isEnabled && isFocusOK },
dependencies: [recallSeed, isFocusOK],
});
useRegisteredHotkeys({
id: 'recallPrompts',
category: 'viewer',
callback: recallPrompts.recall,
options: { enabled: recallPrompts.isEnabled },
dependencies: [recallPrompts],
options: { enabled: recallPrompts.isEnabled && isFocusOK },
dependencies: [recallPrompts, isFocusOK],
});
useRegisteredHotkeys({
id: 'remix',
category: 'viewer',
callback: recallRemix.recall,
options: { enabled: recallRemix.isEnabled },
dependencies: [recallRemix],
options: { enabled: recallRemix.isEnabled && isFocusOK },
dependencies: [recallRemix, isFocusOK],
});
useRegisteredHotkeys({
id: 'useSize',
category: 'viewer',
callback: recallDimensions.recall,
options: { enabled: recallDimensions.isEnabled },
dependencies: [recallDimensions],
options: { enabled: recallDimensions.isEnabled && isFocusOK },
dependencies: [recallDimensions, isFocusOK],
});
return null;

View File

@@ -1,8 +1,8 @@
import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { useCreateStylePresetFromMetadata } from 'features/gallery/hooks/useCreateStylePresetFromMetadata';
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions';
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
import { useRecallRemix } from 'features/gallery/hooks/useRecallRemix';
import { useRecallSeed } from 'features/gallery/hooks/useRecallSeed';
@@ -12,9 +12,9 @@ import {
PiArrowBendUpLeftBold,
PiArrowsCounterClockwiseBold,
PiAsteriskBold,
PiPaintBrushBold,
PiPlantBold,
PiQuotesBold,
PiRulerBold,
} from 'react-icons/pi';
export const ImageMenuItemMetadataRecallActions = memo(() => {
@@ -27,7 +27,7 @@ export const ImageMenuItemMetadataRecallActions = memo(() => {
const recallRemix = useRecallRemix(imageDTO);
const recallPrompts = useRecallPrompts(imageDTO);
const recallSeed = useRecallSeed(imageDTO);
const stylePreset = useCreateStylePresetFromMetadata(imageDTO);
const recallDimensions = useRecallDimensions(imageDTO);
return (
<MenuItem {...subMenu.parentMenuItemProps} icon={<PiArrowBendUpLeftBold />}>
@@ -52,8 +52,8 @@ export const ImageMenuItemMetadataRecallActions = memo(() => {
<MenuItem icon={<PiAsteriskBold />} onClick={recallAll.recall} isDisabled={!recallAll.isEnabled}>
{t('parameters.useAll')}
</MenuItem>
<MenuItem icon={<PiPaintBrushBold />} onClick={stylePreset.create} isDisabled={!stylePreset.isEnabled}>
{t('stylePresets.useForTemplate')}
<MenuItem icon={<PiRulerBold />} onClick={recallDimensions.recall} isDisabled={!recallDimensions.isEnabled}>
{t('parameters.useSize')}
</MenuItem>
</MenuList>
</Menu>

View File

@@ -0,0 +1,20 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
import { useCreateStylePresetFromMetadata } from 'features/gallery/hooks/useCreateStylePresetFromMetadata';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPaintBrushBold } from 'react-icons/pi';
export const ImageMenuItemUseAsPromptTemplate = memo(() => {
const { t } = useTranslation();
const imageDTO = useImageDTOContext();
const stylePreset = useCreateStylePresetFromMetadata(imageDTO);
return (
<MenuItem icon={<PiPaintBrushBold />} onClickCapture={stylePreset.create} isDisabled={!stylePreset.isEnabled}>
{t('stylePresets.useForTemplate')}
</MenuItem>
);
});
ImageMenuItemUseAsPromptTemplate.displayName = 'ImageMenuItemUseAsPromptTemplate';

View File

@@ -21,6 +21,8 @@ import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import type { ImageDTO } from 'services/api/types';
import { ImageMenuItemUseAsPromptTemplate } from './ImageMenuItemUseAsPromptTemplate';
type SingleSelectionMenuItemsProps = {
imageDTO: ImageDTO;
};
@@ -45,6 +47,7 @@ const SingleSelectionMenuItems = ({ imageDTO }: SingleSelectionMenuItemsProps) =
<ImageMenuItemSendToUpscale />
<ImageMenuItemUseForPromptGeneration />
{(tab === 'canvas' || tab === 'generate') && <ImageMenuItemUseAsRefImage />}
<ImageMenuItemUseAsPromptTemplate />
<ImageMenuItemNewCanvasFromImageSubMenu />
{tab === 'canvas' && <ImageMenuItemNewLayerFromImageSubMenu />}
<MenuDivider />

View File

@@ -1,5 +1,4 @@
import { Button, Divider, IconButton, Menu, MenuButton, MenuList } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { useAppSelector } from 'app/store/storeHooks';
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
@@ -11,10 +10,9 @@ import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions'
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
import { useRecallRemix } from 'features/gallery/hooks/useRecallRemix';
import { useRecallSeed } from 'features/gallery/hooks/useRecallSeed';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { PostProcessingPopover } from 'features/parameters/components/PostProcessing/PostProcessingPopover';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { selectShouldShowProgressInViewer } from 'features/ui/store/uiSelectors';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import {
@@ -27,19 +25,12 @@ import {
PiQuotesBold,
PiRulerBold,
} from 'react-icons/pi';
import { useImageDTO } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
import { useImageViewerContext } from './context';
export const CurrentImageButtons = memo(() => {
export const CurrentImageButtons = memo(({ imageDTO }: { imageDTO: ImageDTO }) => {
const { t } = useTranslation();
const ctx = useImageViewerContext();
const hasProgressImage = useStore(ctx.$hasProgressImage);
const shouldShowProgressInViewer = useAppSelector(selectShouldShowProgressInViewer);
const isDisabledOverride = hasProgressImage && shouldShowProgressInViewer;
const imageName = useAppSelector(selectLastSelectedImage);
const imageDTO = useImageDTO(imageName);
const tab = useAppSelector(selectActiveTab);
const isCanvasOrGenerateTab = tab === 'canvas' || tab === 'generate';
const isUpscalingEnabled = useFeatureStatus('upscaling');
@@ -52,8 +43,6 @@ export const CurrentImageButtons = memo(() => {
const editImage = useEditImage(imageDTO);
const deleteImage = useDeleteImage(imageDTO);
console.log(isDisabledOverride, recallSeed.isEnabled);
return (
<>
<Menu isLazy>
@@ -61,7 +50,7 @@ export const CurrentImageButtons = memo(() => {
as={IconButton}
aria-label={t('parameters.imageActions')}
tooltip={t('parameters.imageActions')}
isDisabled={isDisabledOverride || !imageDTO}
isDisabled={!imageDTO}
variant="link"
alignSelf="stretch"
icon={<PiDotsThreeOutlineFill />}
@@ -74,7 +63,7 @@ export const CurrentImageButtons = memo(() => {
<Button
leftIcon={<PiPencilBold />}
onClick={editImage.edit}
isDisabled={isDisabledOverride || !editImage.isEnabled}
isDisabled={!editImage.isEnabled}
variant="link"
size="sm"
alignSelf="stretch"
@@ -89,62 +78,72 @@ export const CurrentImageButtons = memo(() => {
icon={<PiFlowArrowBold />}
tooltip={`${t('nodes.loadWorkflow')} (W)`}
aria-label={`${t('nodes.loadWorkflow')} (W)`}
isDisabled={isDisabledOverride || !loadWorkflow.isEnabled}
isDisabled={!loadWorkflow.isEnabled}
variant="link"
alignSelf="stretch"
onClick={loadWorkflow.load}
/>
<IconButton
icon={<PiArrowsCounterClockwiseBold />}
tooltip={`${t('parameters.remixImage')} (R)`}
aria-label={`${t('parameters.remixImage')} (R)`}
isDisabled={isDisabledOverride || !recallRemix.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallRemix.recall}
/>
<IconButton
icon={<PiQuotesBold />}
tooltip={`${t('parameters.usePrompt')} (P)`}
aria-label={`${t('parameters.usePrompt')} (P)`}
isDisabled={isDisabledOverride || !recallPrompts.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallPrompts.recall}
/>
<IconButton
icon={<PiPlantBold />}
tooltip={`${t('parameters.useSeed')} (S)`}
aria-label={`${t('parameters.useSeed')} (S)`}
isDisabled={isDisabledOverride || !recallSeed.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallSeed.recall}
/>
<IconButton
icon={<PiRulerBold />}
tooltip={`${t('parameters.useSize')} (D)`}
aria-label={`${t('parameters.useSize')} (D)`}
variant="link"
alignSelf="stretch"
onClick={recallDimensions.recall}
isDisabled={isDisabledOverride || !recallDimensions.isEnabled}
/>
<IconButton
icon={<PiAsteriskBold />}
tooltip={`${t('parameters.useAll')} (A)`}
aria-label={`${t('parameters.useAll')} (A)`}
isDisabled={isDisabledOverride || !recallAll.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallAll.recall}
/>
{isCanvasOrGenerateTab && (
<IconButton
icon={<PiArrowsCounterClockwiseBold />}
tooltip={`${t('parameters.remixImage')} (R)`}
aria-label={`${t('parameters.remixImage')} (R)`}
isDisabled={!recallRemix.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallRemix.recall}
/>
)}
{isCanvasOrGenerateTab && (
<IconButton
icon={<PiQuotesBold />}
tooltip={`${t('parameters.usePrompt')} (P)`}
aria-label={`${t('parameters.usePrompt')} (P)`}
isDisabled={!recallPrompts.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallPrompts.recall}
/>
)}
{isCanvasOrGenerateTab && (
<IconButton
icon={<PiPlantBold />}
tooltip={`${t('parameters.useSeed')} (S)`}
aria-label={`${t('parameters.useSeed')} (S)`}
isDisabled={!recallSeed.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallSeed.recall}
/>
)}
{isCanvasOrGenerateTab && (
<IconButton
icon={<PiRulerBold />}
tooltip={`${t('parameters.useSize')} (D)`}
aria-label={`${t('parameters.useSize')} (D)`}
variant="link"
alignSelf="stretch"
onClick={recallDimensions.recall}
isDisabled={!recallDimensions.isEnabled}
/>
)}
{isCanvasOrGenerateTab && (
<IconButton
icon={<PiAsteriskBold />}
tooltip={`${t('parameters.useAll')} (A)`}
aria-label={`${t('parameters.useAll')} (A)`}
isDisabled={!recallAll.isEnabled}
variant="link"
alignSelf="stretch"
onClick={recallAll.recall}
/>
)}
{isUpscalingEnabled && <PostProcessingPopover imageDTO={imageDTO} isDisabled={isDisabledOverride} />}
{isUpscalingEnabled && <PostProcessingPopover imageDTO={imageDTO} isDisabled={false} />}
<Divider orientation="vertical" h={8} mx={2} />
<DeleteImageButton onClick={deleteImage.delete} isDisabled={isDisabledOverride || !deleteImage.isEnabled} />
<DeleteImageButton onClick={deleteImage.delete} isDisabled={!deleteImage.isEnabled} />
</>
);
});

View File

@@ -1,18 +1,24 @@
import { Flex, Spacer } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { memo } from 'react';
import { useImageDTO } from 'services/api/endpoints/images';
import { CurrentImageButtons } from './CurrentImageButtons';
import { ToggleProgressButton } from './ToggleProgressButton';
export const ViewerToolbar = memo(() => {
const imageName = useAppSelector(selectLastSelectedImage);
const imageDTO = useImageDTO(imageName);
return (
<Flex w="full" justifyContent="center" h={8}>
<ToggleProgressButton />
<Spacer />
<CurrentImageButtons />
{imageDTO && <CurrentImageButtons imageDTO={imageDTO} />}
<Spacer />
<ToggleMetadataViewerButton />
{imageDTO && <ToggleMetadataViewerButton />}
</Flex>
);
});

View File

@@ -1,5 +1,4 @@
import { useAppStore } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { useCanvasManagerSafe } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
import { newCanvasFromImage } from 'features/imageActions/actions';
import { toast } from 'features/toast/toast';
@@ -11,8 +10,6 @@ import type { ImageDTO } from 'services/api/types';
export const useEditImage = (imageDTO?: ImageDTO | null) => {
const { t } = useTranslation();
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const { getState, dispatch } = useAppStore();
const canvasManager = useCanvasManagerSafe();
@@ -21,11 +18,8 @@ export const useEditImage = (imageDTO?: ImageDTO | null) => {
if (!imageDTO) {
return false;
}
if (!isGalleryFocused && !isViewerFocused) {
return false;
}
return true;
}, [imageDTO, isGalleryFocused, isViewerFocused]);
}, [imageDTO]);
const edit = useCallback(async () => {
if (!imageDTO) {

View File

@@ -1,32 +1,23 @@
import { useStore } from '@nanostores/react';
import { useIsRegionFocused } from 'common/hooks/focus';
import { $hasTemplates } from 'features/nodes/store/nodesSlice';
import { useLoadWorkflowWithDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog';
import { useCallback, useMemo } from 'react';
import type { ImageDTO } from 'services/api/types';
export const useLoadWorkflow = (imageDTO?: ImageDTO | null) => {
export const useLoadWorkflow = (imageDTO: ImageDTO) => {
const hasTemplates = useStore($hasTemplates);
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const loadWorkflowWithDialog = useLoadWorkflowWithDialog();
const isEnabled = useMemo(() => {
if (!imageDTO) {
return false;
}
if (!imageDTO.has_workflow) {
return false;
}
if (!hasTemplates) {
return false;
}
if (!isGalleryFocused && !isViewerFocused) {
return false;
}
return true;
}, [hasTemplates, imageDTO, isGalleryFocused, isViewerFocused]);
}, [hasTemplates, imageDTO]);
const load = useCallback(() => {
if (!imageDTO) {

View File

@@ -1,5 +1,4 @@
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
@@ -9,17 +8,15 @@ import type { ImageDTO } from 'services/api/types';
import { useClearStylePresetWithToast } from './useClearStylePresetWithToast';
export const useRecallAll = (imageDTO?: ImageDTO | null) => {
export const useRecallAll = (imageDTO: ImageDTO) => {
const store = useAppStore();
const tab = useAppSelector(selectActiveTab);
const { metadata } = useDebouncedMetadata(imageDTO?.image_name);
const { metadata, isLoading } = useDebouncedMetadata(imageDTO.image_name);
const isStaging = useAppSelector(selectIsStaging);
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const clearStylePreset = useClearStylePresetWithToast();
const isEnabled = useMemo(() => {
if (!isGalleryFocused && !isViewerFocused) {
if (isLoading) {
return false;
}
@@ -32,7 +29,7 @@ export const useRecallAll = (imageDTO?: ImageDTO | null) => {
}
return true;
}, [isGalleryFocused, isViewerFocused, metadata, tab]);
}, [isLoading, metadata, tab]);
const handlersToSkip = useMemo(() => {
if (tab === 'canvas' && isStaging) {

View File

@@ -1,27 +1,16 @@
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { MetadataUtils } from 'features/metadata/parsing';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { useCallback, useMemo } from 'react';
import type { ImageDTO } from 'services/api/types';
export const useRecallDimensions = (imageDTO?: ImageDTO | null) => {
export const useRecallDimensions = (imageDTO: ImageDTO) => {
const store = useAppStore();
const tab = useAppSelector(selectActiveTab);
const isStaging = useAppSelector(selectIsStaging);
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const isEnabled = useMemo(() => {
if (!imageDTO) {
return false;
}
if (!isGalleryFocused && !isViewerFocused) {
return false;
}
if (tab !== 'canvas' && tab !== 'generate') {
return false;
}
@@ -31,12 +20,9 @@ export const useRecallDimensions = (imageDTO?: ImageDTO | null) => {
}
return true;
}, [imageDTO, isGalleryFocused, isStaging, isViewerFocused, tab]);
}, [isStaging, tab]);
const recall = useCallback(() => {
if (!imageDTO) {
return;
}
if (!isEnabled) {
return;
}

View File

@@ -1,5 +1,4 @@
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -8,15 +7,13 @@ import type { ImageDTO } from 'services/api/types';
import { useClearStylePresetWithToast } from './useClearStylePresetWithToast';
export const useRecallPrompts = (imageDTO?: ImageDTO | null) => {
export const useRecallPrompts = (imageDTO: ImageDTO) => {
const store = useAppStore();
const tab = useAppSelector(selectActiveTab);
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const clearStylePreset = useClearStylePresetWithToast();
const [hasPrompts, setHasPrompts] = useState(false);
const { metadata } = useDebouncedMetadata(imageDTO?.image_name);
const { metadata, isLoading } = useDebouncedMetadata(imageDTO.image_name);
useEffect(() => {
const parse = async () => {
@@ -42,7 +39,7 @@ export const useRecallPrompts = (imageDTO?: ImageDTO | null) => {
}, [metadata, store]);
const isEnabled = useMemo(() => {
if (!isGalleryFocused && !isViewerFocused) {
if (isLoading) {
return false;
}
@@ -55,7 +52,7 @@ export const useRecallPrompts = (imageDTO?: ImageDTO | null) => {
}
return true;
}, [hasPrompts, isGalleryFocused, isViewerFocused, tab]);
}, [hasPrompts, isLoading, tab]);
const recall = useCallback(() => {
if (!metadata) {

View File

@@ -1,5 +1,4 @@
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
@@ -9,18 +8,16 @@ import type { ImageDTO } from 'services/api/types';
import { useClearStylePresetWithToast } from './useClearStylePresetWithToast';
export const useRecallRemix = (imageDTO?: ImageDTO | null) => {
export const useRecallRemix = (imageDTO: ImageDTO) => {
const store = useAppStore();
const tab = useAppSelector(selectActiveTab);
const isStaging = useAppSelector(selectIsStaging);
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const clearStylePreset = useClearStylePresetWithToast();
const { metadata } = useDebouncedMetadata(imageDTO?.image_name);
const { metadata, isLoading } = useDebouncedMetadata(imageDTO.image_name);
const isEnabled = useMemo(() => {
if (!isGalleryFocused && !isViewerFocused) {
if (isLoading) {
return false;
}
@@ -33,7 +30,7 @@ export const useRecallRemix = (imageDTO?: ImageDTO | null) => {
}
return true;
}, [isGalleryFocused, isViewerFocused, metadata, tab]);
}, [isLoading, metadata, tab]);
const handlersToSkip = useMemo(() => {
// Remix always skips the seed handler

View File

@@ -1,19 +1,16 @@
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
import { useIsRegionFocused } from 'common/hooks/focus';
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
import type { ImageDTO } from 'services/api/types';
export const useRecallSeed = (imageDTO?: ImageDTO | null) => {
export const useRecallSeed = (imageDTO: ImageDTO) => {
const store = useAppStore();
const tab = useAppSelector(selectActiveTab);
const [hasSeed, setHasSeed] = useState(false);
const isGalleryFocused = useIsRegionFocused('gallery');
const isViewerFocused = useIsRegionFocused('viewer');
const { metadata } = useDebouncedMetadata(imageDTO?.image_name);
const { metadata, isLoading } = useDebouncedMetadata(imageDTO.image_name);
useEffect(() => {
const parse = async () => {
@@ -29,7 +26,7 @@ export const useRecallSeed = (imageDTO?: ImageDTO | null) => {
}, [metadata, store]);
const isEnabled = useMemo(() => {
if (!isGalleryFocused && !isViewerFocused) {
if (isLoading) {
return false;
}
@@ -46,7 +43,7 @@ export const useRecallSeed = (imageDTO?: ImageDTO | null) => {
}
return true;
}, [hasSeed, isGalleryFocused, isViewerFocused, metadata, tab]);
}, [hasSeed, isLoading, metadata, tab]);
const recall = useCallback(() => {
if (!metadata) {