diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts index 6bff69c64a3..b1d60edc2dc 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/appStarted.ts @@ -12,27 +12,12 @@ export const appStarted = createAction('app/appStarted'); export const addAppStartedListener = (startAppListening: AppStartListening) => { startAppListening({ actionCreator: appStarted, - effect: (action, { unsubscribe, cancelActiveListeners, take, getState, dispatch }) => { + effect: async (action, { unsubscribe, cancelActiveListeners, take, getState, dispatch }) => { // this should only run once cancelActiveListeners(); unsubscribe(); - // ensure an image is selected when we load the first board - take(imagesApi.endpoints.getImageNames.matchFulfilled).then((firstImageLoad) => { - if (firstImageLoad === null) { - // timeout or cancelled - return; - } - const [{ payload }] = firstImageLoad; - const selectedImage = selectLastSelectedItem(getState()); - if (selectedImage) { - return; - } - if (payload.image_names[0]) { - dispatch(imageSelected(payload.image_names[0])); - } - }); - + // Fire patchmatch check without blocking the image-selection logic below dispatch(appInfoApi.endpoints.getPatchmatchStatus.initiate()) .unwrap() .then((isPatchmatchAvailable) => { @@ -43,6 +28,24 @@ export const addAppStartedListener = (startAppListening: AppStartListening) => { } }) .catch(noop); + + // ensure an image is selected when we load the first board. + // The effect must be async and await take() so that RTK keeps the listener's AbortController + // alive until the query resolves; a synchronous effect causes the controller to be aborted + // immediately after the effect returns, before any network response arrives. + const firstImageLoad = await take(imagesApi.endpoints.getImageNames.matchFulfilled, 5000); + if (firstImageLoad === null) { + // timeout or cancelled + return; + } + const [{ payload }] = firstImageLoad; + const selectedImage = selectLastSelectedItem(getState()); + if (selectedImage) { + return; + } + if (payload.image_names[0]) { + dispatch(imageSelected(payload.image_names[0])); + } }, }); }; diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 9d4d2bfd75d..6a25caadce4 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -191,6 +191,6 @@ export const gallerySliceConfig: SliceConfig = { } return zGalleryState.parse(state); }, - persistDenylist: ['selection', 'selectedBoardId', 'galleryView', 'imageToCompare'], + persistDenylist: ['selection', 'galleryView', 'imageToCompare'], }, };