Skip to content

Commit 5f0a979

Browse files
feat(ui): allow send-image-to-canvas to work when canvas is uninitialized
Add `useCanvasIsBusySafe()` hook. This is like `useCanvasIsBusy()`, but when the canvas is not initialized, it gracefully falls back to false instead of raising. Because app tabs are lazy-loaded, the canvas is not initialized until the user visits that tab. If the page loads up on the workflows tab, the canvas will be uninitialized until the user clicks on it. This graceful fallback behaviour allows actions like sending an image to canvas to work even when the canvas is not yet initialized. These actions are exposed in the image context menu, and previously were hidden when the canvas was not initialized. We can now show these actions and use them even when the canvas is uninitialized. - Add `useCanvasIsBusySafe()` hook - Use the new hook in the image context menu for send to canvas actions - Do not use `<CanvasManagerProviderGate />` in the image context menu (this was hiding the actions when canvas was uninitialized)
1 parent f655a85 commit 5f0a979

File tree

4 files changed

+33
-17
lines changed

4 files changed

+33
-17
lines changed
Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
11
import { useStore } from '@nanostores/react';
2-
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
2+
import { $false } from 'app/store/nanostores/util';
3+
import { useCanvasManager, useCanvasManagerSafe } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
34

5+
/**
6+
* Returns a boolena indicating whether the canvas is busy:
7+
* - While staging
8+
* - While an entity is transforming
9+
* - While an entity is filtering
10+
* - While the canvas is doing some other long-running operation, like rasterizing a layer
11+
*
12+
* This hook will throw an error if the canvas manager is not initialized.
13+
*/
414
export const useCanvasIsBusy = () => {
515
const canvasManager = useCanvasManager();
6-
/**
7-
* Whether the canvas is busy:
8-
* - While staging
9-
* - While an entity is transforming
10-
* - While an entity is filtering
11-
* - While the canvas is doing some other long-running operation, like rasterizing a layer
12-
*/
1316
const isBusy = useStore(canvasManager.$isBusy);
1417

1518
return isBusy;
1619
};
20+
21+
/**
22+
* Returns a boolena indicating whether the canvas is busy:
23+
* - While staging
24+
* - While an entity is transforming
25+
* - While an entity is filtering
26+
* - While the canvas is doing some other long-running operation, like rasterizing a layer
27+
*
28+
* This hook will fall back to false if the canvas manager is not initialized.
29+
*/
30+
export const useCanvasIsBusySafe = () => {
31+
const canvasManager = useCanvasManagerSafe();
32+
const isBusy = useStore(canvasManager?.$isBusy ?? $false);
33+
34+
return isBusy;
35+
};

invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewCanvasFromImageSubMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
22
import { useAppStore } from 'app/store/nanostores/store';
33
import { useAppSelector } from 'app/store/storeHooks';
44
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
5-
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
5+
import { useCanvasIsBusySafe } from 'features/controlLayers/hooks/useCanvasIsBusy';
66
import { selectIsSD3 } from 'features/controlLayers/store/paramsSlice';
77
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
88
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
@@ -19,7 +19,7 @@ export const ImageMenuItemNewCanvasFromImageSubMenu = memo(() => {
1919
const store = useAppStore();
2020
const imageDTO = useImageDTOContext();
2121
const imageViewer = useImageViewer();
22-
const isBusy = useCanvasIsBusy();
22+
const isBusy = useCanvasIsBusySafe();
2323
const isSD3 = useAppSelector(selectIsSD3);
2424

2525
const onClickNewCanvasWithRasterLayerFromImage = useCallback(() => {

invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageMenuItemNewLayerFromImageSubMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useAppStore } from 'app/store/nanostores/store';
33
import { useAppSelector } from 'app/store/storeHooks';
44
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
55
import { NewLayerIcon } from 'features/controlLayers/components/common/icons';
6-
import { useCanvasIsBusy } from 'features/controlLayers/hooks/useCanvasIsBusy';
6+
import { useCanvasIsBusySafe } from 'features/controlLayers/hooks/useCanvasIsBusy';
77
import { selectIsFLUX, selectIsSD3 } from 'features/controlLayers/store/paramsSlice';
88
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
99
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
@@ -21,7 +21,7 @@ export const ImageMenuItemNewLayerFromImageSubMenu = memo(() => {
2121
const store = useAppStore();
2222
const imageDTO = useImageDTOContext();
2323
const imageViewer = useImageViewer();
24-
const isBusy = useCanvasIsBusy();
24+
const isBusy = useCanvasIsBusySafe();
2525
const isFLUX = useAppSelector(selectIsFLUX);
2626
const isSD3 = useAppSelector(selectIsSD3);
2727

invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { MenuDivider } from '@invoke-ai/ui-library';
22
import { IconMenuItemGroup } from 'common/components/IconMenuItem';
3-
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
43
import { ImageMenuItemChangeBoard } from 'features/gallery/components/ImageContextMenu/ImageMenuItemChangeBoard';
54
import { ImageMenuItemCopy } from 'features/gallery/components/ImageContextMenu/ImageMenuItemCopy';
65
import { ImageMenuItemDelete } from 'features/gallery/components/ImageContextMenu/ImageMenuItemDelete';
@@ -38,10 +37,8 @@ const SingleSelectionMenuItems = ({ imageDTO }: SingleSelectionMenuItemsProps) =
3837
<ImageMenuItemMetadataRecallActions />
3938
<MenuDivider />
4039
<ImageMenuItemSendToUpscale />
41-
<CanvasManagerProviderGate>
42-
<ImageMenuItemNewCanvasFromImageSubMenu />
43-
<ImageMenuItemNewLayerFromImageSubMenu />
44-
</CanvasManagerProviderGate>
40+
<ImageMenuItemNewCanvasFromImageSubMenu />
41+
<ImageMenuItemNewLayerFromImageSubMenu />
4542
<MenuDivider />
4643
<ImageMenuItemChangeBoard />
4744
<ImageMenuItemStarUnstar />

0 commit comments

Comments
 (0)