diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index da8d69c91e..9c890304a4 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -1073,8 +1073,9 @@
},
"toast": {
"addedToBoard": "Added to board",
- "baseModelChangedCleared_one": "Base model changed, cleared or disabled {{count}} incompatible submodel",
- "baseModelChangedCleared_other": "Base model changed, cleared or disabled {{count}} incompatible submodels",
+ "baseModelChanged": "Base Model Changed",
+ "baseModelChangedCleared_one": "Cleared or disabled {{count}} incompatible submodel",
+ "baseModelChangedCleared_other": "Cleared or disabled {{count}} incompatible submodels",
"canceled": "Processing Canceled",
"canvasCopiedClipboard": "Canvas Copied to Clipboard",
"canvasDownloaded": "Canvas Downloaded",
@@ -1095,10 +1096,17 @@
"metadataLoadFailed": "Failed to load metadata",
"modelAddedSimple": "Model Added to Queue",
"modelImportCanceled": "Model Import Canceled",
+ "outOfMemoryError": "Out of Memory Error",
+ "outOfMemoryErrorDesc": "Your current generation settings exceed system capacity. Please adjust your settings and try again.",
"parameters": "Parameters",
- "parameterNotSet": "{{parameter}} not set",
- "parameterSet": "{{parameter}} set",
- "parametersNotSet": "Parameters Not Set",
+ "parameterSet": "Parameter Recalled",
+ "parameterSetDesc": "Recalled {{parameter}}",
+ "parameterNotSet": "Parameter Recalled",
+ "parameterNotSetDesc": "Unable to recall {{parameter}}",
+ "parameterNotSetDescWithMessage": "Unable to recall {{parameter}}: {{message}}",
+ "parametersSet": "Parameters Recalled",
+ "parametersNotSet": "Parameters Not Recalled",
+ "errorCopied": "Error Copied",
"problemCopyingCanvas": "Problem Copying Canvas",
"problemCopyingCanvasDesc": "Unable to export base layer",
"problemCopyingImage": "Unable to Copy Image",
@@ -1118,11 +1126,13 @@
"sentToImageToImage": "Sent To Image To Image",
"sentToUnifiedCanvas": "Sent to Unified Canvas",
"serverError": "Server Error",
+ "sessionRef": "Session: {{sessionId}}",
"setAsCanvasInitialImage": "Set as canvas initial image",
"setCanvasInitialImage": "Set canvas initial image",
"setControlImage": "Set as control image",
"setInitialImage": "Set as initial image",
"setNodeField": "Set as node field",
+ "somethingWentWrong": "Something Went Wrong",
"uploadFailed": "Upload failed",
"uploadFailedInvalidUploadDesc": "Must be single PNG or JPEG image",
"uploadInitialImage": "Upload Initial Image",
diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx
index 1ff093f348..2d878d96e7 100644
--- a/invokeai/frontend/web/src/app/components/App.tsx
+++ b/invokeai/frontend/web/src/app/components/App.tsx
@@ -25,7 +25,6 @@ import { useGetOpenAPISchemaQuery } from 'services/api/endpoints/appInfo';
import AppErrorBoundaryFallback from './AppErrorBoundaryFallback';
import PreselectedImage from './PreselectedImage';
-import Toaster from './Toaster';
const DEFAULT_CONFIG = {};
@@ -96,7 +95,6 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
-
);
diff --git a/invokeai/frontend/web/src/app/components/AppErrorBoundaryFallback.tsx b/invokeai/frontend/web/src/app/components/AppErrorBoundaryFallback.tsx
index d2992a8cd9..7f1f825ba3 100644
--- a/invokeai/frontend/web/src/app/components/AppErrorBoundaryFallback.tsx
+++ b/invokeai/frontend/web/src/app/components/AppErrorBoundaryFallback.tsx
@@ -1,4 +1,5 @@
-import { Button, Flex, Heading, Link, Text, useToast } from '@invoke-ai/ui-library';
+import { Button, Flex, Heading, Link, Text } from '@invoke-ai/ui-library';
+import { toast } from 'features/toast/toast';
import newGithubIssueUrl from 'new-github-issue-url';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -11,16 +12,16 @@ type Props = {
};
const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
- const toast = useToast();
const { t } = useTranslation();
const handleCopy = useCallback(() => {
const text = JSON.stringify(serializeError(error), null, 2);
navigator.clipboard.writeText(`\`\`\`\n${text}\n\`\`\``);
toast({
- title: 'Error Copied',
+ id: 'ERROR_COPIED',
+ title: t('toast.errorCopied'),
});
- }, [error, toast]);
+ }, [error, t]);
const url = useMemo(
() =>
diff --git a/invokeai/frontend/web/src/app/components/Toaster.ts b/invokeai/frontend/web/src/app/components/Toaster.ts
deleted file mode 100644
index c86fd5060d..0000000000
--- a/invokeai/frontend/web/src/app/components/Toaster.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { useToast } from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast, clearToastQueue } from 'features/system/store/systemSlice';
-import type { MakeToastArg } from 'features/system/util/makeToast';
-import { makeToast } from 'features/system/util/makeToast';
-import { memo, useCallback, useEffect } from 'react';
-
-/**
- * Logical component. Watches the toast queue and makes toasts when the queue is not empty.
- * @returns null
- */
-const Toaster = () => {
- const dispatch = useAppDispatch();
- const toastQueue = useAppSelector((s) => s.system.toastQueue);
- const toast = useToast();
- useEffect(() => {
- toastQueue.forEach((t) => {
- toast(t);
- });
- toastQueue.length > 0 && dispatch(clearToastQueue());
- }, [dispatch, toast, toastQueue]);
-
- return null;
-};
-
-/**
- * Returns a function that can be used to make a toast.
- * @example
- * const toaster = useAppToaster();
- * toaster('Hello world!');
- * toaster({ title: 'Hello world!', status: 'success' });
- * @returns A function that can be used to make a toast.
- * @see makeToast
- * @see MakeToastArg
- * @see UseToastOptions
- */
-export const useAppToaster = () => {
- const dispatch = useAppDispatch();
- const toaster = useCallback((arg: MakeToastArg) => dispatch(addToast(makeToast(arg))), [dispatch]);
-
- return toaster;
-};
-
-export default memo(Toaster);
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
index 0c0c8ed2bc..77345be5d3 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts
@@ -41,12 +41,10 @@ import { addGeneratorProgressEventListener } from 'app/store/middleware/listener
import { addGraphExecutionStateCompleteEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketGraphExecutionStateComplete';
import { addInvocationCompleteEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete';
import { addInvocationErrorEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError';
-import { addInvocationRetrievalErrorEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationRetrievalError';
import { addInvocationStartedEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationStarted';
import { addModelInstallEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketModelInstall';
import { addModelLoadEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketModelLoad';
import { addSocketQueueItemStatusChangedEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged';
-import { addSessionRetrievalErrorEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketSessionRetrievalError';
import { addSocketSubscribedEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketSubscribed';
import { addSocketUnsubscribedEventListener } from 'app/store/middleware/listenerMiddleware/listeners/socketio/socketUnsubscribed';
import { addStagingAreaImageSavedListener } from 'app/store/middleware/listenerMiddleware/listeners/stagingAreaImageSaved';
@@ -114,8 +112,6 @@ addSocketSubscribedEventListener(startAppListening);
addSocketUnsubscribedEventListener(startAppListening);
addModelLoadEventListener(startAppListening);
addModelInstallEventListener(startAppListening);
-addSessionRetrievalErrorEventListener(startAppListening);
-addInvocationRetrievalErrorEventListener(startAppListening);
addSocketQueueItemStatusChangedEventListener(startAppListening);
addBulkDownloadListeners(startAppListening);
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts
index ae26531722..9095a08431 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts
@@ -8,7 +8,7 @@ import {
resetCanvas,
setInitialCanvasImage,
} from 'features/canvas/store/canvasSlice';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { queueApi } from 'services/api/endpoints/queue';
@@ -30,22 +30,20 @@ export const addCommitStagingAreaImageListener = (startAppListening: AppStartLis
req.reset();
if (canceled > 0) {
log.debug(`Canceled ${canceled} canvas batches`);
- dispatch(
- addToast({
- title: t('queue.cancelBatchSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'CANCEL_BATCH_SUCCEEDED',
+ title: t('queue.cancelBatchSucceeded'),
+ status: 'success',
+ });
}
dispatch(canvasBatchIdsReset());
} catch {
log.error('Failed to cancel canvas batches');
- dispatch(
- addToast({
- title: t('queue.cancelBatchFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'CANCEL_BATCH_FAILED',
+ title: t('queue.cancelBatchFailed'),
+ status: 'error',
+ });
}
},
});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts
index 68eda997b7..3f74bf9b61 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts
@@ -1,8 +1,8 @@
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { parseify } from 'common/util/serialize';
-import { toast } from 'common/util/toast';
import { zPydanticValidationError } from 'features/system/store/zodSchemas';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { truncate, upperFirst } from 'lodash-es';
import { queueApi } from 'services/api/endpoints/queue';
@@ -16,18 +16,15 @@ export const addBatchEnqueuedListener = (startAppListening: AppStartListening) =
const arg = action.meta.arg.originalArgs;
logger('queue').debug({ enqueueResult: parseify(response) }, 'Batch enqueued');
- if (!toast.isActive('batch-queued')) {
- toast({
- id: 'batch-queued',
- title: t('queue.batchQueued'),
- description: t('queue.batchQueuedDesc', {
- count: response.enqueued,
- direction: arg.prepend ? t('queue.front') : t('queue.back'),
- }),
- duration: 1000,
- status: 'success',
- });
- }
+ toast({
+ id: 'QUEUE_BATCH_SUCCEEDED',
+ title: t('queue.batchQueued'),
+ status: 'success',
+ description: t('queue.batchQueuedDesc', {
+ count: response.enqueued,
+ direction: arg.prepend ? t('queue.front') : t('queue.back'),
+ }),
+ });
},
});
@@ -40,9 +37,10 @@ export const addBatchEnqueuedListener = (startAppListening: AppStartListening) =
if (!response) {
toast({
+ id: 'QUEUE_BATCH_FAILED',
title: t('queue.batchFailedToQueue'),
status: 'error',
- description: 'Unknown Error',
+ description: t('common.unknownError'),
});
logger('queue').error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
return;
@@ -52,7 +50,7 @@ export const addBatchEnqueuedListener = (startAppListening: AppStartListening) =
if (result.success) {
result.data.data.detail.map((e) => {
toast({
- id: 'batch-failed-to-queue',
+ id: 'QUEUE_BATCH_FAILED',
title: truncate(upperFirst(e.msg), { length: 128 }),
status: 'error',
description: truncate(
@@ -64,9 +62,10 @@ export const addBatchEnqueuedListener = (startAppListening: AppStartListening) =
});
} else if (response.status !== 403) {
toast({
+ id: 'QUEUE_BATCH_FAILED',
title: t('queue.batchFailedToQueue'),
- description: t('common.unknownError'),
status: 'error',
+ description: t('common.unknownError'),
});
}
logger('queue').error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx
index 38a0fd7911..51945081ea 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx
@@ -1,8 +1,7 @@
-import type { UseToastOptions } from '@invoke-ai/ui-library';
import { ExternalLink } from '@invoke-ai/ui-library';
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
-import { toast } from 'common/util/toast';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import {
@@ -28,7 +27,6 @@ export const addBulkDownloadListeners = (startAppListening: AppStartListening) =
// Show the response message if it exists, otherwise show the default message
description: action.payload.response || t('gallery.bulkDownloadRequestedDesc'),
duration: null,
- isClosable: true,
});
},
});
@@ -40,9 +38,9 @@ export const addBulkDownloadListeners = (startAppListening: AppStartListening) =
// There isn't any toast to update if we get this event.
toast({
+ id: 'BULK_DOWNLOAD_REQUEST_FAILED',
title: t('gallery.bulkDownloadRequestFailed'),
- status: 'success',
- isClosable: true,
+ status: 'error',
});
},
});
@@ -65,7 +63,7 @@ export const addBulkDownloadListeners = (startAppListening: AppStartListening) =
// TODO(psyche): This URL may break in in some environments (e.g. Nvidia workbench) but we need to test it first
const url = `/api/v1/images/download/${bulk_download_item_name}`;
- const toastOptions: UseToastOptions = {
+ toast({
id: bulk_download_item_name,
title: t('gallery.bulkDownloadReady', 'Download ready'),
status: 'success',
@@ -77,14 +75,7 @@ export const addBulkDownloadListeners = (startAppListening: AppStartListening) =
/>
),
duration: null,
- isClosable: true,
- };
-
- if (toast.isActive(bulk_download_item_name)) {
- toast.update(bulk_download_item_name, toastOptions);
- } else {
- toast(toastOptions);
- }
+ });
},
});
@@ -95,20 +86,13 @@ export const addBulkDownloadListeners = (startAppListening: AppStartListening) =
const { bulk_download_item_name } = action.payload.data;
- const toastOptions: UseToastOptions = {
+ toast({
id: bulk_download_item_name,
title: t('gallery.bulkDownloadFailed'),
status: 'error',
description: action.payload.data.error,
duration: null,
- isClosable: true,
- };
-
- if (toast.isActive(bulk_download_item_name)) {
- toast.update(bulk_download_item_name, toastOptions);
- } else {
- toast(toastOptions);
- }
+ });
},
});
};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasCopiedToClipboard.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasCopiedToClipboard.ts
index e1f4804d56..311dda3e2e 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasCopiedToClipboard.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasCopiedToClipboard.ts
@@ -2,14 +2,14 @@ import { $logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { canvasCopiedToClipboard } from 'features/canvas/store/actions';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
-import { addToast } from 'features/system/store/systemSlice';
import { copyBlobToClipboard } from 'features/system/util/copyBlobToClipboard';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
export const addCanvasCopiedToClipboardListener = (startAppListening: AppStartListening) => {
startAppListening({
actionCreator: canvasCopiedToClipboard,
- effect: async (action, { dispatch, getState }) => {
+ effect: async (action, { getState }) => {
const moduleLog = $logger.get().child({ namespace: 'canvasCopiedToClipboardListener' });
const state = getState();
@@ -19,22 +19,20 @@ export const addCanvasCopiedToClipboardListener = (startAppListening: AppStartLi
copyBlobToClipboard(blob);
} catch (err) {
moduleLog.error(String(err));
- dispatch(
- addToast({
- title: t('toast.problemCopyingCanvas'),
- description: t('toast.problemCopyingCanvasDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'CANVAS_COPY_FAILED',
+ title: t('toast.problemCopyingCanvas'),
+ description: t('toast.problemCopyingCanvasDesc'),
+ status: 'error',
+ });
return;
}
- dispatch(
- addToast({
- title: t('toast.canvasCopiedClipboard'),
- status: 'success',
- })
- );
+ toast({
+ id: 'CANVAS_COPY_SUCCEEDED',
+ title: t('toast.canvasCopiedClipboard'),
+ status: 'success',
+ });
},
});
};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasDownloadedAsImage.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasDownloadedAsImage.ts
index 5b8150bd20..71e616b9ea 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasDownloadedAsImage.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasDownloadedAsImage.ts
@@ -3,13 +3,13 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { canvasDownloadedAsImage } from 'features/canvas/store/actions';
import { downloadBlob } from 'features/canvas/util/downloadBlob';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
export const addCanvasDownloadedAsImageListener = (startAppListening: AppStartListening) => {
startAppListening({
actionCreator: canvasDownloadedAsImage,
- effect: async (action, { dispatch, getState }) => {
+ effect: async (action, { getState }) => {
const moduleLog = $logger.get().child({ namespace: 'canvasSavedToGalleryListener' });
const state = getState();
@@ -18,18 +18,17 @@ export const addCanvasDownloadedAsImageListener = (startAppListening: AppStartLi
blob = await getBaseLayerBlob(state);
} catch (err) {
moduleLog.error(String(err));
- dispatch(
- addToast({
- title: t('toast.problemDownloadingCanvas'),
- description: t('toast.problemDownloadingCanvasDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'CANVAS_DOWNLOAD_FAILED',
+ title: t('toast.problemDownloadingCanvas'),
+ description: t('toast.problemDownloadingCanvasDesc'),
+ status: 'error',
+ });
return;
}
downloadBlob(blob, 'canvas.png');
- dispatch(addToast({ title: t('toast.canvasDownloaded'), status: 'success' }));
+ toast({ id: 'CANVAS_DOWNLOAD_SUCCEEDED', title: t('toast.canvasDownloaded'), status: 'success' });
},
});
};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts
index 55392ebff4..2aa1f52d6c 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts
@@ -3,7 +3,7 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { canvasImageToControlAdapter } from 'features/canvas/store/actions';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { controlAdapterImageChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
@@ -20,13 +20,12 @@ export const addCanvasImageToControlNetListener = (startAppListening: AppStartLi
blob = await getBaseLayerBlob(state, true);
} catch (err) {
log.error(String(err));
- dispatch(
- addToast({
- title: t('toast.problemSavingCanvas'),
- description: t('toast.problemSavingCanvasDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PROBLEM_SAVING_CANVAS',
+ title: t('toast.problemSavingCanvas'),
+ description: t('toast.problemSavingCanvasDesc'),
+ status: 'error',
+ });
return;
}
@@ -43,7 +42,7 @@ export const addCanvasImageToControlNetListener = (startAppListening: AppStartLi
crop_visible: false,
postUploadAction: {
type: 'TOAST',
- toastOptions: { title: t('toast.canvasSentControlnetAssets') },
+ title: t('toast.canvasSentControlnetAssets'),
},
})
).unwrap();
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskSavedToGallery.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskSavedToGallery.ts
index af0c3878fc..454342b997 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskSavedToGallery.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskSavedToGallery.ts
@@ -2,7 +2,7 @@ import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { canvasMaskSavedToGallery } from 'features/canvas/store/actions';
import { getCanvasData } from 'features/canvas/util/getCanvasData';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
@@ -29,13 +29,12 @@ export const addCanvasMaskSavedToGalleryListener = (startAppListening: AppStartL
if (!maskBlob) {
log.error('Problem getting mask layer blob');
- dispatch(
- addToast({
- title: t('toast.problemSavingMask'),
- description: t('toast.problemSavingMaskDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PROBLEM_SAVING_MASK',
+ title: t('toast.problemSavingMask'),
+ description: t('toast.problemSavingMaskDesc'),
+ status: 'error',
+ });
return;
}
@@ -52,7 +51,7 @@ export const addCanvasMaskSavedToGalleryListener = (startAppListening: AppStartL
crop_visible: true,
postUploadAction: {
type: 'TOAST',
- toastOptions: { title: t('toast.maskSavedAssets') },
+ title: t('toast.maskSavedAssets'),
},
})
);
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskToControlNet.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskToControlNet.ts
index 569b4badc7..2e6ca61d8a 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskToControlNet.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMaskToControlNet.ts
@@ -3,7 +3,7 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { canvasMaskToControlAdapter } from 'features/canvas/store/actions';
import { getCanvasData } from 'features/canvas/util/getCanvasData';
import { controlAdapterImageChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
@@ -30,13 +30,12 @@ export const addCanvasMaskToControlNetListener = (startAppListening: AppStartLis
if (!maskBlob) {
log.error('Problem getting mask layer blob');
- dispatch(
- addToast({
- title: t('toast.problemImportingMask'),
- description: t('toast.problemImportingMaskDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PROBLEM_IMPORTING_MASK',
+ title: t('toast.problemImportingMask'),
+ description: t('toast.problemImportingMaskDesc'),
+ status: 'error',
+ });
return;
}
@@ -53,7 +52,7 @@ export const addCanvasMaskToControlNetListener = (startAppListening: AppStartLis
crop_visible: false,
postUploadAction: {
type: 'TOAST',
- toastOptions: { title: t('toast.maskSentControlnetAssets') },
+ title: t('toast.maskSentControlnetAssets'),
},
})
).unwrap();
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts
index 71b0e62b44..9ae6de2e76 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasMerged.ts
@@ -4,7 +4,7 @@ import { canvasMerged } from 'features/canvas/store/actions';
import { $canvasBaseLayer } from 'features/canvas/store/canvasNanostore';
import { setMergedCanvas } from 'features/canvas/store/canvasSlice';
import { getFullBaseLayerBlob } from 'features/canvas/util/getFullBaseLayerBlob';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
@@ -17,13 +17,12 @@ export const addCanvasMergedListener = (startAppListening: AppStartListening) =>
if (!blob) {
moduleLog.error('Problem getting base layer blob');
- dispatch(
- addToast({
- title: t('toast.problemMergingCanvas'),
- description: t('toast.problemMergingCanvasDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PROBLEM_MERGING_CANVAS',
+ title: t('toast.problemMergingCanvas'),
+ description: t('toast.problemMergingCanvasDesc'),
+ status: 'error',
+ });
return;
}
@@ -31,13 +30,12 @@ export const addCanvasMergedListener = (startAppListening: AppStartListening) =>
if (!canvasBaseLayer) {
moduleLog.error('Problem getting canvas base layer');
- dispatch(
- addToast({
- title: t('toast.problemMergingCanvas'),
- description: t('toast.problemMergingCanvasDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PROBLEM_MERGING_CANVAS',
+ title: t('toast.problemMergingCanvas'),
+ description: t('toast.problemMergingCanvasDesc'),
+ status: 'error',
+ });
return;
}
@@ -54,7 +52,7 @@ export const addCanvasMergedListener = (startAppListening: AppStartListening) =>
is_intermediate: true,
postUploadAction: {
type: 'TOAST',
- toastOptions: { title: t('toast.canvasMerged') },
+ title: t('toast.canvasMerged'),
},
})
).unwrap();
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts
index 7f456e9a68..71586b5f6e 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery.ts
@@ -3,7 +3,7 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { parseify } from 'common/util/serialize';
import { canvasSavedToGallery } from 'features/canvas/store/actions';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
@@ -19,13 +19,12 @@ export const addCanvasSavedToGalleryListener = (startAppListening: AppStartListe
blob = await getBaseLayerBlob(state);
} catch (err) {
log.error(String(err));
- dispatch(
- addToast({
- title: t('toast.problemSavingCanvas'),
- description: t('toast.problemSavingCanvasDesc'),
- status: 'error',
- })
- );
+ toast({
+ id: 'CANVAS_SAVE_FAILED',
+ title: t('toast.problemSavingCanvas'),
+ description: t('toast.problemSavingCanvasDesc'),
+ status: 'error',
+ });
return;
}
@@ -42,7 +41,7 @@ export const addCanvasSavedToGalleryListener = (startAppListening: AppStartListe
crop_visible: true,
postUploadAction: {
type: 'TOAST',
- toastOptions: { title: t('toast.canvasSavedGallery') },
+ title: t('toast.canvasSavedGallery'),
},
metadata: {
_canvas_objects: parseify(state.canvas.layerState.objects),
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts
index 3dc8db93f9..8d67f509d8 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts
@@ -14,7 +14,7 @@ import {
} from 'features/controlLayers/store/controlLayersSlice';
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
import { isImageOutput } from 'features/nodes/types/common';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast, ToastID } from 'features/toast/toast';
import { t } from 'i18next';
import { isEqual } from 'lodash-es';
import { getImageDTO } from 'services/api/endpoints/images';
@@ -174,12 +174,11 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni
}
}
- dispatch(
- addToast({
- title: t('queue.graphFailedToQueue'),
- status: 'error',
- })
- );
+ toast({
+ id: ToastID.GRAPH_QUEUE_FAILED,
+ title: t('queue.graphFailedToQueue'),
+ status: 'error',
+ });
}
} finally {
req.reset();
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts
index 0055866aa7..e6c83badcf 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed.ts
@@ -10,7 +10,7 @@ import {
} from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { isImageOutput } from 'features/nodes/types/common';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast, ToastID } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import { queueApi } from 'services/api/endpoints/queue';
@@ -108,12 +108,11 @@ export const addControlNetImageProcessedListener = (startAppListening: AppStartL
}
}
- dispatch(
- addToast({
- title: t('queue.graphFailedToQueue'),
- status: 'error',
- })
- );
+ toast({
+ id: ToastID.GRAPH_QUEUE_FAILED,
+ title: t('queue.graphFailedToQueue'),
+ status: 'error',
+ });
}
},
});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
index d5d74bf668..cd5304c32b 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts
@@ -1,4 +1,3 @@
-import type { UseToastOptions } from '@invoke-ai/ui-library';
import { logger } from 'app/logging/logger';
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
@@ -14,7 +13,7 @@ import {
} from 'features/controlLayers/store/controlLayersSlice';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { omit } from 'lodash-es';
import { boardsApi } from 'services/api/endpoints/boards';
@@ -42,16 +41,17 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
return;
}
- const DEFAULT_UPLOADED_TOAST: UseToastOptions = {
+ const DEFAULT_UPLOADED_TOAST = {
+ id: 'IMAGE_UPLOADED',
title: t('toast.imageUploaded'),
status: 'success',
- };
+ } as const;
// default action - just upload and alert user
if (postUploadAction?.type === 'TOAST') {
- const { toastOptions } = postUploadAction;
if (!autoAddBoardId || autoAddBoardId === 'none') {
- dispatch(addToast({ ...DEFAULT_UPLOADED_TOAST, ...toastOptions }));
+ const title = postUploadAction.title || DEFAULT_UPLOADED_TOAST.title;
+ toast({ ...DEFAULT_UPLOADED_TOAST, title });
} else {
// Add this image to the board
dispatch(
@@ -70,24 +70,20 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
? `${t('toast.addedToBoard')} ${board.board_name}`
: `${t('toast.addedToBoard')} ${autoAddBoardId}`;
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description,
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description,
+ });
}
return;
}
if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') {
dispatch(setInitialCanvasImage(imageDTO, selectOptimalDimension(state)));
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description: t('toast.setAsCanvasInitialImage'),
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: t('toast.setAsCanvasInitialImage'),
+ });
return;
}
@@ -105,68 +101,56 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
controlImage: imageDTO.image_name,
})
);
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description: t('toast.setControlImage'),
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: t('toast.setControlImage'),
+ });
return;
}
if (postUploadAction?.type === 'SET_CA_LAYER_IMAGE') {
const { layerId } = postUploadAction;
dispatch(caLayerImageChanged({ layerId, imageDTO }));
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description: t('toast.setControlImage'),
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: t('toast.setControlImage'),
+ });
}
if (postUploadAction?.type === 'SET_IPA_LAYER_IMAGE') {
const { layerId } = postUploadAction;
dispatch(ipaLayerImageChanged({ layerId, imageDTO }));
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description: t('toast.setControlImage'),
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: t('toast.setControlImage'),
+ });
}
if (postUploadAction?.type === 'SET_RG_LAYER_IP_ADAPTER_IMAGE') {
const { layerId, ipAdapterId } = postUploadAction;
dispatch(rgLayerIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description: t('toast.setControlImage'),
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: t('toast.setControlImage'),
+ });
}
if (postUploadAction?.type === 'SET_II_LAYER_IMAGE') {
const { layerId } = postUploadAction;
dispatch(iiLayerImageChanged({ layerId, imageDTO }));
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description: t('toast.setControlImage'),
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: t('toast.setControlImage'),
+ });
}
if (postUploadAction?.type === 'SET_NODES_IMAGE') {
const { nodeId, fieldName } = postUploadAction;
dispatch(fieldImageValueChanged({ nodeId, fieldName, value: imageDTO }));
- dispatch(
- addToast({
- ...DEFAULT_UPLOADED_TOAST,
- description: `${t('toast.setNodeField')} ${fieldName}`,
- })
- );
+ toast({
+ ...DEFAULT_UPLOADED_TOAST,
+ description: `${t('toast.setNodeField')} ${fieldName}`,
+ });
return;
}
},
@@ -174,7 +158,7 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
startAppListening({
matcher: imagesApi.endpoints.uploadImage.matchRejected,
- effect: (action, { dispatch }) => {
+ effect: (action) => {
const log = logger('images');
const sanitizedData = {
arg: {
@@ -183,13 +167,11 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
},
};
log.error({ ...sanitizedData }, 'Image upload failed');
- dispatch(
- addToast({
- title: t('toast.imageUploadFailed'),
- description: action.error.message,
- status: 'error',
- })
- );
+ toast({
+ title: t('toast.imageUploadFailed'),
+ description: action.error.message,
+ status: 'error',
+ });
},
});
};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
index bc049cf498..239a5b863d 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts
@@ -8,8 +8,7 @@ import { loraRemoved } from 'features/lora/store/loraSlice';
import { modelSelected } from 'features/parameters/store/actions';
import { modelChanged, vaeSelected } from 'features/parameters/store/generationSlice';
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { forEach } from 'lodash-es';
@@ -60,16 +59,14 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
});
if (modelsCleared > 0) {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.baseModelChangedCleared', {
- count: modelsCleared,
- }),
- status: 'warning',
- })
- )
- );
+ toast({
+ id: 'BASE_MODEL_CHANGED',
+ title: t('toast.baseModelChanged'),
+ description: t('toast.baseModelChangedCleared', {
+ count: modelsCleared,
+ }),
+ status: 'warning',
+ });
}
}
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts
index 61a978d576..81ecfaddf8 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/setDefaultSettings.ts
@@ -19,8 +19,7 @@ import {
isParameterWidth,
zParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast, ToastID } from 'features/toast/toast';
import { t } from 'i18next';
import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models';
import { isNonRefinerMainModelConfig } from 'services/api/types';
@@ -109,7 +108,7 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
}
}
- dispatch(addToast(makeToast({ title: t('toast.parameterSet', { parameter: 'Default settings' }) })));
+ toast({ id: ToastID.PARAMETER_SET, title: t('toast.parameterSet', { parameter: 'Default settings' }) });
}
},
});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts
index ce26c4dd7d..c2d85395e9 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts
@@ -3,6 +3,10 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { deepClone } from 'common/util/deepClone';
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState';
import { zNodeStatus } from 'features/nodes/types/invocation';
+import { toast } from 'features/toast/toast';
+import ToastWithSessionRefDescription from 'features/toast/ToastWithSessionRefDescription';
+import { t } from 'i18next';
+import { startCase } from 'lodash-es';
import { socketInvocationError } from 'services/events/actions';
const log = logger('socketio');
@@ -12,7 +16,7 @@ export const addInvocationErrorEventListener = (startAppListening: AppStartListe
actionCreator: socketInvocationError,
effect: (action) => {
log.error(action.payload, `Invocation error (${action.payload.data.node.type})`);
- const { source_node_id } = action.payload.data;
+ const { source_node_id, error_type } = action.payload.data;
const nes = deepClone($nodeExecutionStates.get()[source_node_id]);
if (nes) {
nes.status = zNodeStatus.enum.FAILED;
@@ -21,6 +25,30 @@ export const addInvocationErrorEventListener = (startAppListening: AppStartListe
nes.progressImage = null;
upsertExecutionState(nes.nodeId, nes);
}
+ const errorType = startCase(action.payload.data.error_type);
+ const sessionId = action.payload.data.graph_execution_state_id;
+
+ if (error_type === 'OutOfMemoryError') {
+ toast({
+ id: 'INVOCATION_ERROR',
+ title: t('toast.outOfMemoryError'),
+ status: 'error',
+ description: ToastWithSessionRefDescription({
+ message: t('toast.outOfMemoryDescription'),
+ sessionId,
+ }),
+ });
+ } else {
+ toast({
+ id: `INVOCATION_ERROR_${errorType}`,
+ title: t('toast.serverError'),
+ status: 'error',
+ description: ToastWithSessionRefDescription({
+ message: errorType,
+ sessionId,
+ }),
+ });
+ }
},
});
};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationRetrievalError.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationRetrievalError.ts
deleted file mode 100644
index 44da4c0ddb..0000000000
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationRetrievalError.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { logger } from 'app/logging/logger';
-import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
-import { socketInvocationRetrievalError } from 'services/events/actions';
-
-const log = logger('socketio');
-
-export const addInvocationRetrievalErrorEventListener = (startAppListening: AppStartListening) => {
- startAppListening({
- actionCreator: socketInvocationRetrievalError,
- effect: (action) => {
- log.error(action.payload, `Invocation retrieval error (${action.payload.data.graph_execution_state_id})`);
- },
- });
-};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketSessionRetrievalError.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketSessionRetrievalError.ts
deleted file mode 100644
index a1a497dc08..0000000000
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketSessionRetrievalError.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { logger } from 'app/logging/logger';
-import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
-import { socketSessionRetrievalError } from 'services/events/actions';
-
-const log = logger('socketio');
-
-export const addSessionRetrievalErrorEventListener = (startAppListening: AppStartListening) => {
- startAppListening({
- actionCreator: socketSessionRetrievalError,
- effect: (action) => {
- log.error(action.payload, `Session retrieval error (${action.payload.data.graph_execution_state_id})`);
- },
- });
-};
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/stagingAreaImageSaved.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/stagingAreaImageSaved.ts
index 6816e25bc1..6c4c2a9df1 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/stagingAreaImageSaved.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/stagingAreaImageSaved.ts
@@ -1,6 +1,6 @@
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
import { stagingAreaImageSaved } from 'features/canvas/store/actions';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
@@ -29,15 +29,14 @@ export const addStagingAreaImageSavedListener = (startAppListening: AppStartList
})
);
}
- dispatch(addToast({ title: t('toast.imageSaved'), status: 'success' }));
+ toast({ id: 'IMAGE_SAVED', title: t('toast.imageSaved'), status: 'success' });
} catch (error) {
- dispatch(
- addToast({
- title: t('toast.imageSavingFailed'),
- description: (error as Error)?.message,
- status: 'error',
- })
- );
+ toast({
+ id: 'IMAGE_SAVE_FAILED',
+ title: t('toast.imageSavingFailed'),
+ description: (error as Error)?.message,
+ status: 'error',
+ });
}
},
});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts
index 05cc2f8e83..07df2a4f42 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts
@@ -5,8 +5,7 @@ import { $templates, nodesChanged } from 'features/nodes/store/nodesSlice';
import { NodeUpdateError } from 'features/nodes/types/error';
import { isInvocationNode } from 'features/nodes/types/invocation';
import { getNeedsUpdate, updateNode } from 'features/nodes/util/node/nodeUpdate';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
export const addUpdateAllNodesRequestedListener = (startAppListening: AppStartListening) => {
@@ -50,24 +49,18 @@ export const addUpdateAllNodesRequestedListener = (startAppListening: AppStartLi
count: unableToUpdateCount,
})
);
- dispatch(
- addToast(
- makeToast({
- title: t('nodes.unableToUpdateNodes', {
- count: unableToUpdateCount,
- }),
- })
- )
- );
+ toast({
+ id: 'UNABLE_TO_UPDATE_NODES',
+ title: t('nodes.unableToUpdateNodes', {
+ count: unableToUpdateCount,
+ }),
+ });
} else {
- dispatch(
- addToast(
- makeToast({
- title: t('nodes.allNodesUpdated'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'ALL_NODES_UPDATED',
+ title: t('nodes.allNodesUpdated'),
+ status: 'success',
+ });
}
},
});
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/upscaleRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/upscaleRequested.ts
index ff5d5f24be..67d016244d 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/upscaleRequested.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/upscaleRequested.ts
@@ -4,7 +4,7 @@ import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'
import { parseify } from 'common/util/serialize';
import { buildAdHocUpscaleGraph } from 'features/nodes/util/graph/buildAdHocUpscaleGraph';
import { createIsAllowedToUpscaleSelector } from 'features/parameters/hooks/useIsAllowedToUpscale';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast, ToastID } from 'features/toast/toast';
import { t } from 'i18next';
import { queueApi } from 'services/api/endpoints/queue';
import type { BatchConfig, ImageDTO } from 'services/api/types';
@@ -29,12 +29,11 @@ export const addUpscaleRequestedListener = (startAppListening: AppStartListening
{ imageDTO },
t(detailTKey ?? 'parameters.isAllowedToUpscale.tooLarge') // should never coalesce
);
- dispatch(
- addToast({
- title: t(detailTKey ?? 'parameters.isAllowedToUpscale.tooLarge'), // should never coalesce
- status: 'error',
- })
- );
+ toast({
+ id: 'NOT_ALLOWED_TO_UPSCALE',
+ title: t(detailTKey ?? 'parameters.isAllowedToUpscale.tooLarge'), // should never coalesce
+ status: 'error',
+ });
return;
}
@@ -65,12 +64,11 @@ export const addUpscaleRequestedListener = (startAppListening: AppStartListening
if (error instanceof Object && 'status' in error && error.status === 403) {
return;
} else {
- dispatch(
- addToast({
- title: t('queue.graphFailedToQueue'),
- status: 'error',
- })
- );
+ toast({
+ id: ToastID.GRAPH_QUEUE_FAILED,
+ title: t('queue.graphFailedToQueue'),
+ status: 'error',
+ });
}
}
},
diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts
index 9f9e70fc01..e6fc5a526a 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts
@@ -8,8 +8,7 @@ import type { Templates } from 'features/nodes/store/types';
import { WorkflowMigrationError, WorkflowVersionError } from 'features/nodes/types/error';
import { graphToWorkflow } from 'features/nodes/util/workflow/graphToWorkflow';
import { validateWorkflow } from 'features/nodes/util/workflow/validateWorkflow';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { checkBoardAccess, checkImageAccess, checkModelAccess } from 'services/api/hooks/accessChecks';
import type { GraphAndWorkflowResponse, NonNullableGraph } from 'services/api/types';
@@ -49,23 +48,18 @@ export const addWorkflowLoadRequestedListener = (startAppListening: AppStartList
dispatch(workflowLoaded(workflow));
if (!warnings.length) {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.workflowLoaded'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'WORKFLOW_LOADED',
+ title: t('toast.workflowLoaded'),
+ status: 'success',
+ });
} else {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.loadedWithWarnings'),
- status: 'warning',
- })
- )
- );
+ toast({
+ id: 'WORKFLOW_LOADED',
+ title: t('toast.loadedWithWarnings'),
+ status: 'warning',
+ });
+
warnings.forEach(({ message, ...rest }) => {
log.warn(rest, message);
});
@@ -78,54 +72,42 @@ export const addWorkflowLoadRequestedListener = (startAppListening: AppStartList
if (e instanceof WorkflowVersionError) {
// The workflow version was not recognized in the valid list of versions
log.error({ error: parseify(e) }, e.message);
- dispatch(
- addToast(
- makeToast({
- title: t('nodes.unableToValidateWorkflow'),
- status: 'error',
- description: e.message,
- })
- )
- );
+ toast({
+ id: 'UNABLE_TO_VALIDATE_WORKFLOW',
+ title: t('nodes.unableToValidateWorkflow'),
+ status: 'error',
+ description: e.message,
+ });
} else if (e instanceof WorkflowMigrationError) {
// There was a problem migrating the workflow to the latest version
log.error({ error: parseify(e) }, e.message);
- dispatch(
- addToast(
- makeToast({
- title: t('nodes.unableToValidateWorkflow'),
- status: 'error',
- description: e.message,
- })
- )
- );
+ toast({
+ id: 'UNABLE_TO_VALIDATE_WORKFLOW',
+ title: t('nodes.unableToValidateWorkflow'),
+ status: 'error',
+ description: e.message,
+ });
} else if (e instanceof z.ZodError) {
// There was a problem validating the workflow itself
const { message } = fromZodError(e, {
prefix: t('nodes.workflowValidation'),
});
log.error({ error: parseify(e) }, message);
- dispatch(
- addToast(
- makeToast({
- title: t('nodes.unableToValidateWorkflow'),
- status: 'error',
- description: message,
- })
- )
- );
+ toast({
+ id: 'UNABLE_TO_VALIDATE_WORKFLOW',
+ title: t('nodes.unableToValidateWorkflow'),
+ status: 'error',
+ description: message,
+ });
} else {
// Some other error occurred
log.error({ error: parseify(e) }, t('nodes.unknownErrorValidatingWorkflow'));
- dispatch(
- addToast(
- makeToast({
- title: t('nodes.unableToValidateWorkflow'),
- status: 'error',
- description: t('nodes.unknownErrorValidatingWorkflow'),
- })
- )
- );
+ toast({
+ id: 'UNABLE_TO_VALIDATE_WORKFLOW',
+ title: t('nodes.unableToValidateWorkflow'),
+ status: 'error',
+ description: t('nodes.unknownErrorValidatingWorkflow'),
+ });
}
}
},
diff --git a/invokeai/frontend/web/src/common/hooks/useCopyImageToClipboard.ts b/invokeai/frontend/web/src/common/hooks/useCopyImageToClipboard.ts
index ef9db44a9d..233b841034 100644
--- a/invokeai/frontend/web/src/common/hooks/useCopyImageToClipboard.ts
+++ b/invokeai/frontend/web/src/common/hooks/useCopyImageToClipboard.ts
@@ -1,11 +1,10 @@
-import { useAppToaster } from 'app/components/Toaster';
import { useImageUrlToBlob } from 'common/hooks/useImageUrlToBlob';
import { copyBlobToClipboard } from 'features/system/util/copyBlobToClipboard';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
export const useCopyImageToClipboard = () => {
- const toaster = useAppToaster();
const { t } = useTranslation();
const imageUrlToBlob = useImageUrlToBlob();
@@ -16,12 +15,11 @@ export const useCopyImageToClipboard = () => {
const copyImageToClipboard = useCallback(
async (image_url: string) => {
if (!isClipboardAPIAvailable) {
- toaster({
+ toast({
+ id: 'PROBLEM_COPYING_IMAGE',
title: t('toast.problemCopyingImage'),
description: "Your browser doesn't support the Clipboard API.",
status: 'error',
- duration: 2500,
- isClosable: true,
});
}
try {
@@ -33,23 +31,21 @@ export const useCopyImageToClipboard = () => {
copyBlobToClipboard(blob);
- toaster({
+ toast({
+ id: 'IMAGE_COPIED',
title: t('toast.imageCopied'),
status: 'success',
- duration: 2500,
- isClosable: true,
});
} catch (err) {
- toaster({
+ toast({
+ id: 'PROBLEM_COPYING_IMAGE',
title: t('toast.problemCopyingImage'),
description: String(err),
status: 'error',
- duration: 2500,
- isClosable: true,
});
}
},
- [imageUrlToBlob, isClipboardAPIAvailable, t, toaster]
+ [imageUrlToBlob, isClipboardAPIAvailable, t]
);
return { isClipboardAPIAvailable, copyImageToClipboard };
diff --git a/invokeai/frontend/web/src/common/hooks/useDownloadImage.ts b/invokeai/frontend/web/src/common/hooks/useDownloadImage.ts
index 26a17e1d0c..ede247b9fb 100644
--- a/invokeai/frontend/web/src/common/hooks/useDownloadImage.ts
+++ b/invokeai/frontend/web/src/common/hooks/useDownloadImage.ts
@@ -1,13 +1,12 @@
import { useStore } from '@nanostores/react';
-import { useAppToaster } from 'app/components/Toaster';
import { $authToken } from 'app/store/nanostores/authToken';
import { useAppDispatch } from 'app/store/storeHooks';
import { imageDownloaded } from 'features/gallery/store/actions';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const useDownloadImage = () => {
- const toaster = useAppToaster();
const { t } = useTranslation();
const dispatch = useAppDispatch();
const authToken = useStore($authToken);
@@ -37,16 +36,15 @@ export const useDownloadImage = () => {
window.URL.revokeObjectURL(url);
dispatch(imageDownloaded());
} catch (err) {
- toaster({
+ toast({
+ id: 'PROBLEM_DOWNLOADING_IMAGE',
title: t('toast.problemDownloadingImage'),
description: String(err),
status: 'error',
- duration: 2500,
- isClosable: true,
});
}
},
- [t, toaster, dispatch, authToken]
+ [t, dispatch, authToken]
);
return { downloadImage };
diff --git a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts
index 0334294e98..5b1bf1f5b3 100644
--- a/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts
+++ b/invokeai/frontend/web/src/common/hooks/useFullscreenDropzone.ts
@@ -1,6 +1,6 @@
-import { useAppToaster } from 'app/components/Toaster';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useCallback, useEffect, useState } from 'react';
import type { Accept, FileRejection } from 'react-dropzone';
@@ -26,7 +26,6 @@ const selectPostUploadAction = createMemoizedSelector(activeTabNameSelector, (ac
export const useFullscreenDropzone = () => {
const { t } = useTranslation();
- const toaster = useAppToaster();
const postUploadAction = useAppSelector(selectPostUploadAction);
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
const [isHandlingUpload, setIsHandlingUpload] = useState(false);
@@ -37,13 +36,14 @@ export const useFullscreenDropzone = () => {
(rejection: FileRejection) => {
setIsHandlingUpload(true);
- toaster({
+ toast({
+ id: 'UPLOAD_FAILED',
title: t('toast.uploadFailed'),
description: rejection.errors.map((error) => error.message).join('\n'),
status: 'error',
});
},
- [t, toaster]
+ [t]
);
const fileAcceptedCallback = useCallback(
@@ -62,7 +62,8 @@ export const useFullscreenDropzone = () => {
const onDrop = useCallback(
(acceptedFiles: Array, fileRejections: Array) => {
if (fileRejections.length > 1) {
- toaster({
+ toast({
+ id: 'UPLOAD_FAILED',
title: t('toast.uploadFailed'),
description: t('toast.uploadFailedInvalidUploadDesc'),
status: 'error',
@@ -78,7 +79,7 @@ export const useFullscreenDropzone = () => {
fileAcceptedCallback(file);
});
},
- [t, toaster, fileAcceptedCallback, fileRejectionCallback]
+ [t, fileAcceptedCallback, fileRejectionCallback]
);
const onDragOver = useCallback(() => {
diff --git a/invokeai/frontend/web/src/common/util/toast.ts b/invokeai/frontend/web/src/common/util/toast.ts
deleted file mode 100644
index ac61a4a12d..0000000000
--- a/invokeai/frontend/web/src/common/util/toast.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { createStandaloneToast, theme, TOAST_OPTIONS } from '@invoke-ai/ui-library';
-
-export const { toast } = createStandaloneToast({
- theme: theme,
- defaultOptions: TOAST_OPTIONS.defaultOptions,
-});
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
index f5063ea717..b3119aa8fa 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx
@@ -1,6 +1,5 @@
import { Flex, MenuDivider, MenuItem, Spinner } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
-import { useAppToaster } from 'app/components/Toaster';
import { $customStarUI } from 'app/store/nanostores/customStarUI';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
@@ -14,6 +13,7 @@ import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/ac
import { $templates } from 'features/nodes/store/nodesSlice';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
+import { toast } from 'features/toast/toast';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { useGetAndLoadEmbeddedWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow';
import { size } from 'lodash-es';
@@ -46,7 +46,6 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
const optimalDimension = useAppSelector(selectOptimalDimension);
const dispatch = useAppDispatch();
const { t } = useTranslation();
- const toaster = useAppToaster();
const isCanvasEnabled = useFeatureStatus('canvas');
const customStarUi = useStore($customStarUI);
const { downloadImage } = useDownloadImage();
@@ -86,13 +85,12 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
});
dispatch(setInitialCanvasImage(imageDTO, optimalDimension));
- toaster({
+ toast({
+ id: 'SENT_TO_CANVAS',
title: t('toast.sentToUnifiedCanvas'),
status: 'success',
- duration: 2500,
- isClosable: true,
});
- }, [dispatch, imageDTO, t, toaster, optimalDimension]);
+ }, [dispatch, imageDTO, t, optimalDimension]);
const handleChangeBoard = useCallback(() => {
dispatch(imagesToChangeSelected([imageDTO]));
diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx
index 4bf55116db..df3fbe2765 100644
--- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx
+++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx
@@ -1,6 +1,5 @@
import { IconButton } from '@invoke-ai/ui-library';
import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppToaster } from 'app/components/Toaster';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
import { setShouldShowImageDetails } from 'features/ui/store/uiSlice';
@@ -14,7 +13,6 @@ export const ToggleMetadataViewerButton = memo(() => {
const dispatch = useAppDispatch();
const shouldShowImageDetails = useAppSelector((s) => s.ui.shouldShowImageDetails);
const lastSelectedImage = useAppSelector(selectLastSelectedImage);
- const toaster = useAppToaster();
const { t } = useTranslation();
const { currentData: imageDTO } = useGetImageDTOQuery(lastSelectedImage?.image_name ?? skipToken);
@@ -24,7 +22,7 @@ export const ToggleMetadataViewerButton = memo(() => {
[dispatch, shouldShowImageDetails]
);
- useHotkeys('i', toggleMetadataViewer, { enabled: Boolean(imageDTO) }, [imageDTO, shouldShowImageDetails, toaster]);
+ useHotkeys('i', toggleMetadataViewer, { enabled: Boolean(imageDTO) }, [imageDTO, shouldShowImageDetails]);
return (
= async (layers) => {
return `${layers.length} ${t('controlLayers.layers', { count: layers.length })}`;
};
-const parameterSetToast = (parameter: string, description?: string) => {
+const parameterSetToast = (parameter: string) => {
toast({
- title: t('toast.parameterSet', { parameter }),
- description,
+ id: ToastID.PARAMETER_SET,
+ title: t('toast.parameterSet'),
+ description: t('toast.parameterSetDesc', { parameter }),
status: 'info',
- duration: 2500,
- isClosable: true,
});
};
-const parameterNotSetToast = (parameter: string, description?: string) => {
+const parameterNotSetToast = (parameter: string, message?: string) => {
toast({
- title: t('toast.parameterNotSet', { parameter }),
- description,
+ id: 'PARAMETER_NOT_SET',
+ title: t('toast.parameterNotSet'),
+ description: message
+ ? t('toast.parameterNotSetDescWithMessage', { parameter, message })
+ : t('toast.parameterNotSetDesc', { parameter }),
status: 'warning',
- duration: 2500,
- isClosable: true,
});
};
@@ -458,7 +458,18 @@ export const parseAndRecallAllMetadata = async (
});
})
);
+
if (results.some((result) => result.status === 'fulfilled')) {
- parameterSetToast(t('toast.parameters'));
+ toast({
+ id: ToastID.PARAMETER_SET,
+ title: t('toast.parametersSet'),
+ status: 'info',
+ });
+ } else {
+ toast({
+ id: ToastID.PARAMETER_SET,
+ title: t('toast.parametersNotSet'),
+ status: 'warning',
+ });
}
};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx
index 184429478e..b3c456494f 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceForm.tsx
@@ -1,7 +1,5 @@
import { Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast, ToastID } from 'features/toast/toast';
import type { ChangeEventHandler } from 'react';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -14,7 +12,6 @@ export const HuggingFaceForm = () => {
const [displayResults, setDisplayResults] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const [_getHuggingFaceModels, { isLoading, data }] = useLazyGetHuggingFaceModelsQuery();
const [installModel] = useInstallModelMutation();
@@ -24,29 +21,23 @@ export const HuggingFaceForm = () => {
installModel({ source })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelAddedSimple'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUED,
+ title: t('toast.modelAddedSimple'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUE_FAILED,
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
},
- [installModel, dispatch, t]
+ [installModel, t]
);
const getModels = useCallback(async () => {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx
index 1595a61147..225782aa3d 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResultItem.tsx
@@ -1,7 +1,5 @@
import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast, ToastID } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
@@ -12,7 +10,6 @@ type Props = {
};
export const HuggingFaceResultItem = ({ result }: Props) => {
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const [installModel] = useInstallModelMutation();
@@ -20,28 +17,22 @@ export const HuggingFaceResultItem = ({ result }: Props) => {
installModel({ source: result })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelAddedSimple'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUED,
+ title: t('toast.modelAddedSimple'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUE_FAILED,
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
- }, [installModel, result, dispatch, t]);
+ }, [installModel, result, t]);
return (
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx
index 8144accf3f..3d78e76b83 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/HuggingFaceFolder/HuggingFaceResults.tsx
@@ -8,10 +8,8 @@ import {
InputGroup,
InputRightElement,
} from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast, ToastID } from 'features/toast/toast';
import type { ChangeEventHandler } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -27,7 +25,6 @@ type HuggingFaceResultsProps = {
export const HuggingFaceResults = ({ results }: HuggingFaceResultsProps) => {
const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState('');
- const dispatch = useAppDispatch();
const [installModel] = useInstallModelMutation();
@@ -51,29 +48,23 @@ export const HuggingFaceResults = ({ results }: HuggingFaceResultsProps) => {
installModel({ source: result })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelAddedSimple'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUED,
+ title: t('toast.modelAddedSimple'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUE_FAILED,
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
}
- }, [filteredResults, installModel, dispatch, t]);
+ }, [filteredResults, installModel, t]);
return (
<>
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx
index 282e07ee27..8f1a6c38b8 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/InstallModelForm.tsx
@@ -1,7 +1,5 @@
import { Button, Checkbox, Flex, FormControl, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast, ToastID } from 'features/toast/toast';
import { t } from 'i18next';
import { useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form';
@@ -14,8 +12,6 @@ type SimpleImportModelConfig = {
};
export const InstallModelForm = () => {
- const dispatch = useAppDispatch();
-
const [installModel, { isLoading }] = useInstallModelMutation();
const { register, handleSubmit, formState, reset } = useForm({
@@ -35,31 +31,25 @@ export const InstallModelForm = () => {
installModel({ source: values.location, inplace: values.inplace })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelAddedSimple'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUED,
+ title: t('toast.modelAddedSimple'),
+ status: 'success',
+ });
reset(undefined, { keepValues: true });
})
.catch((error) => {
reset(undefined, { keepValues: true });
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUE_FAILED,
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
},
- [dispatch, reset, installModel]
+ [reset, installModel]
);
return (
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
index 5db2743669..b3544af5b3 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueue.tsx
@@ -1,8 +1,6 @@
import { Box, Button, Flex, Heading } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { useCallback, useMemo } from 'react';
import { useListModelInstallsQuery, usePruneCompletedModelInstallsMutation } from 'services/api/endpoints/models';
@@ -10,8 +8,6 @@ import { useListModelInstallsQuery, usePruneCompletedModelInstallsMutation } fro
import { ModelInstallQueueItem } from './ModelInstallQueueItem';
export const ModelInstallQueue = () => {
- const dispatch = useAppDispatch();
-
const { data } = useListModelInstallsQuery();
const [_pruneCompletedModelInstalls] = usePruneCompletedModelInstallsMutation();
@@ -20,28 +16,22 @@ export const ModelInstallQueue = () => {
_pruneCompletedModelInstalls()
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.prunedQueue'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'MODEL_INSTALL_QUEUE_PRUNED',
+ title: t('toast.prunedQueue'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: 'MODEL_INSTALL_QUEUE_PRUNE_FAILED',
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
- }, [_pruneCompletedModelInstalls, dispatch]);
+ }, [_pruneCompletedModelInstalls]);
const pruneAvailable = useMemo(() => {
return data?.some(
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx
index d1fc600510..82a28b2d75 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelInstallQueue/ModelInstallQueueItem.tsx
@@ -1,7 +1,5 @@
import { Flex, IconButton, Progress, Text, Tooltip } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { isNil } from 'lodash-es';
import { useCallback, useMemo } from 'react';
@@ -29,7 +27,6 @@ const formatBytes = (bytes: number) => {
export const ModelInstallQueueItem = (props: ModelListItemProps) => {
const { installJob } = props;
- const dispatch = useAppDispatch();
const [deleteImportModel] = useCancelModelInstallMutation();
@@ -37,28 +34,22 @@ export const ModelInstallQueueItem = (props: ModelListItemProps) => {
deleteImportModel(installJob.id)
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelImportCanceled'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'MODEL_INSTALL_CANCELED',
+ title: t('toast.modelImportCanceled'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: 'MODEL_INSTALL_CANCEL_FAILED',
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
- }, [deleteImportModel, installJob, dispatch]);
+ }, [deleteImportModel, installJob]);
const sourceLocation = useMemo(() => {
switch (installJob.source.type) {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx
index 360d6c1403..67261730e5 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanFolder/ScanFolderResults.tsx
@@ -11,10 +11,8 @@ import {
InputGroup,
InputRightElement,
} from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast, ToastID } from 'features/toast/toast';
import type { ChangeEvent, ChangeEventHandler } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -30,7 +28,6 @@ type ScanModelResultsProps = {
export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
const { t } = useTranslation();
const [searchTerm, setSearchTerm] = useState('');
- const dispatch = useAppDispatch();
const [inplace, setInplace] = useState(true);
const [installModel] = useInstallModelMutation();
@@ -61,58 +58,46 @@ export const ScanModelsResults = ({ results }: ScanModelResultsProps) => {
installModel({ source: result.path, inplace })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelAddedSimple'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUED,
+ title: t('toast.modelAddedSimple'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUE_FAILED,
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
}
- }, [filteredResults, installModel, inplace, dispatch, t]);
+ }, [filteredResults, installModel, inplace, t]);
const handleInstallOne = useCallback(
(source: string) => {
installModel({ source, inplace })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelAddedSimple'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUED,
+ title: t('toast.modelAddedSimple'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUE_FAILED,
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
},
- [installModel, inplace, dispatch, t]
+ [installModel, inplace, t]
);
return (
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx
index a32fad810f..27a96c494c 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/StarterModels/StartModelsResultItem.tsx
@@ -1,8 +1,6 @@
import { Badge, Box, Flex, IconButton, Text } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
import ModelBaseBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast, ToastID } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi';
@@ -14,7 +12,6 @@ type Props = {
};
export const StarterModelsResultItem = ({ result }: Props) => {
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const allSources = useMemo(() => {
const _allSources = [result.source];
if (result.dependencies) {
@@ -29,29 +26,23 @@ export const StarterModelsResultItem = ({ result }: Props) => {
installModel({ source })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('toast.modelAddedSimple'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUED,
+ title: t('toast.modelAddedSimple'),
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: ToastID.MODEL_INSTALL_QUEUE_FAILED,
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
}
- }, [allSources, installModel, dispatch, t]);
+ }, [allSources, installModel, t]);
return (
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
index c9d0c03ed8..a4a6d5c833 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
@@ -4,8 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
import ModelBaseBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
import ModelFormatBadge from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelFormatBadge';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import type { MouseEvent } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -53,25 +52,19 @@ const ModelListItem = (props: ModelListItemProps) => {
deleteModel({ key: model.key })
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: `${t('modelManager.modelDeleted')}: ${model.name}`,
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'MODEL_DELETED',
+ title: `${t('modelManager.modelDeleted')}: ${model.name}`,
+ status: 'success',
+ });
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${t('modelManager.modelDeleteFailed')}: ${model.name}`,
- status: 'error',
- })
- )
- );
+ toast({
+ id: 'MODEL_DELETE_FAILED',
+ title: `${t('modelManager.modelDeleteFailed')}: ${model.name}`,
+ status: 'error',
+ });
}
});
dispatch(setSelectedModelKey(null));
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx
index dcdc4e2a36..9a84fbc726 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/ControlNetOrT2IAdapterDefaultSettings.tsx
@@ -1,10 +1,9 @@
import { Button, Flex, Heading, SimpleGrid, Text } from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { useAppSelector } from 'app/store/storeHooks';
import { useControlNetOrT2IAdapterDefaultSettings } from 'features/modelManagerV2/hooks/useControlNetOrT2IAdapterDefaultSettings';
import { DefaultPreprocessor } from 'features/modelManagerV2/subpanels/ModelPanel/ControlNetOrT2IAdapterDefaultSettings/DefaultPreprocessor';
import type { FormField } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
@@ -19,7 +18,6 @@ export type ControlNetOrT2IAdapterDefaultSettingsFormData = {
export const ControlNetOrT2IAdapterDefaultSettings = () => {
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const { defaultSettingsDefaults, isLoading: isLoadingDefaultSettings } =
useControlNetOrT2IAdapterDefaultSettings(selectedModelKey);
@@ -46,30 +44,24 @@ export const ControlNetOrT2IAdapterDefaultSettings = () => {
})
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.defaultSettingsSaved'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'DEFAULT_SETTINGS_SAVED',
+ title: t('modelManager.defaultSettingsSaved'),
+ status: 'success',
+ });
reset(data);
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: 'DEFAULT_SETTINGS_SAVE_FAILED',
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
},
- [selectedModelKey, dispatch, reset, updateModel, t]
+ [selectedModelKey, reset, updateModel, t]
);
if (isLoadingDefaultSettings) {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx
index 0d7920ef77..292835a7b7 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Fields/ModelImageUpload.tsx
@@ -1,8 +1,6 @@
import { Box, Button, Flex, Icon, IconButton, Image, Tooltip } from '@invoke-ai/ui-library';
-import { useAppDispatch } from 'app/store/storeHooks';
import { typedMemo } from 'common/util/typedMemo';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { useTranslation } from 'react-i18next';
@@ -15,7 +13,6 @@ type Props = {
};
const ModelImageUpload = ({ model_key, model_image }: Props) => {
- const dispatch = useAppDispatch();
const [image, setImage] = useState(model_image || null);
const { t } = useTranslation();
@@ -34,27 +31,21 @@ const ModelImageUpload = ({ model_key, model_image }: Props) => {
.unwrap()
.then(() => {
setImage(URL.createObjectURL(file));
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.modelImageUpdated'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'MODEL_IMAGE_UPDATED',
+ title: t('modelManager.modelImageUpdated'),
+ status: 'success',
+ });
})
- .catch((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.modelImageUpdateFailed'),
- status: 'error',
- })
- )
- );
+ .catch(() => {
+ toast({
+ id: 'MODEL_IMAGE_UPDATE_FAILED',
+ title: t('modelManager.modelImageUpdateFailed'),
+ status: 'error',
+ });
});
},
- [dispatch, model_key, t, updateModelImage]
+ [model_key, t, updateModelImage]
);
const handleResetImage = useCallback(() => {
@@ -65,26 +56,20 @@ const ModelImageUpload = ({ model_key, model_image }: Props) => {
deleteModelImage(model_key)
.unwrap()
.then(() => {
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.modelImageDeleted'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'MODEL_IMAGE_DELETED',
+ title: t('modelManager.modelImageDeleted'),
+ status: 'success',
+ });
})
- .catch((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.modelImageDeleteFailed'),
- status: 'error',
- })
- )
- );
+ .catch(() => {
+ toast({
+ id: 'MODEL_IMAGE_DELETE_FAILED',
+ title: t('modelManager.modelImageDeleteFailed'),
+ status: 'error',
+ });
});
- }, [dispatch, model_key, t, deleteModelImage]);
+ }, [model_key, t, deleteModelImage]);
const { getInputProps, getRootProps } = useDropzone({
accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'] },
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx
index e096b11209..233fc7bc6b 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/MainModelDefaultSettings.tsx
@@ -1,11 +1,10 @@
import { Button, Flex, Heading, SimpleGrid, Text } from '@invoke-ai/ui-library';
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { useAppSelector } from 'app/store/storeHooks';
import { useMainModelDefaultSettings } from 'features/modelManagerV2/hooks/useMainModelDefaultSettings';
import { DefaultHeight } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultHeight';
import { DefaultWidth } from 'features/modelManagerV2/subpanels/ModelPanel/MainModelDefaultSettings/DefaultWidth';
import type { ParameterScheduler } from 'features/parameters/types/parameterSchemas';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
@@ -39,7 +38,6 @@ export type MainModelDefaultSettingsFormData = {
export const MainModelDefaultSettings = () => {
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const {
defaultSettingsDefaults,
@@ -76,30 +74,24 @@ export const MainModelDefaultSettings = () => {
})
.unwrap()
.then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.defaultSettingsSaved'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'DEFAULT_SETTINGS_SAVED',
+ title: t('modelManager.defaultSettingsSaved'),
+ status: 'success',
+ });
reset(data);
})
.catch((error) => {
if (error) {
- dispatch(
- addToast(
- makeToast({
- title: `${error.data.detail} `,
- status: 'error',
- })
- )
- );
+ toast({
+ id: 'DEFAULT_SETTINGS_SAVE_FAILED',
+ title: `${error.data.detail} `,
+ status: 'error',
+ });
}
});
},
- [selectedModelKey, dispatch, reset, updateModel, t]
+ [selectedModelKey, reset, updateModel, t]
);
if (isLoadingDefaultSettings) {
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx
index d95eed8d24..fa7ca4c394 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/Model.tsx
@@ -4,8 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setSelectedModelMode } from 'features/modelManagerV2/store/modelManagerV2Slice';
import { ModelConvertButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton';
import { ModelEditButton } from 'features/modelManagerV2/subpanels/ModelPanel/ModelEditButton';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
@@ -47,25 +46,19 @@ export const Model = () => {
.then((payload) => {
form.reset(payload, { keepDefaultValues: true });
dispatch(setSelectedModelMode('view'));
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.modelUpdated'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'MODEL_UPDATED',
+ title: t('modelManager.modelUpdated'),
+ status: 'success',
+ });
})
.catch((_) => {
form.reset();
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.modelUpdateFailed'),
- status: 'error',
- })
- )
- );
+ toast({
+ id: 'MODEL_UPDATE_FAILED',
+ title: t('modelManager.modelUpdateFailed'),
+ status: 'error',
+ });
});
},
[dispatch, data?.key, form, t, updateModel]
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx
index bcff0451d6..40ffca76b4 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelConvertButton.tsx
@@ -9,9 +9,7 @@ import {
useDisclosure,
} from '@invoke-ai/ui-library';
import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppDispatch } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useConvertModelMutation, useGetModelConfigQuery } from 'services/api/endpoints/models';
@@ -22,7 +20,6 @@ interface ModelConvertProps {
export const ModelConvertButton = (props: ModelConvertProps) => {
const { modelKey } = props;
- const dispatch = useAppDispatch();
const { t } = useTranslation();
const { data } = useGetModelConfigQuery(modelKey ?? skipToken);
const [convertModel, { isLoading }] = useConvertModelMutation();
@@ -33,38 +30,26 @@ export const ModelConvertButton = (props: ModelConvertProps) => {
return;
}
- dispatch(
- addToast(
- makeToast({
- title: `${t('modelManager.convertingModelBegin')}: ${data?.name}`,
- status: 'info',
- })
- )
- );
+ const toastId = `CONVERTING_MODEL_${data.key}`;
+ toast({
+ id: toastId,
+ title: `${t('modelManager.convertingModelBegin')}: ${data?.name}`,
+ status: 'info',
+ });
convertModel(data?.key)
.unwrap()
.then(() => {
- dispatch(
- addToast(
- makeToast({
- title: `${t('modelManager.modelConverted')}: ${data?.name}`,
- status: 'success',
- })
- )
- );
+ toast({ id: toastId, title: `${t('modelManager.modelConverted')}: ${data?.name}`, status: 'success' });
})
.catch(() => {
- dispatch(
- addToast(
- makeToast({
- title: `${t('modelManager.modelConversionFailed')}: ${data?.name}`,
- status: 'error',
- })
- )
- );
+ toast({
+ id: toastId,
+ title: `${t('modelManager.modelConversionFailed')}: ${data?.name}`,
+ status: 'error',
+ });
});
- }, [data, isLoading, dispatch, t, convertModel]);
+ }, [data, isLoading, t, convertModel]);
if (data?.format !== 'checkpoint') {
return;
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx
index 226a8f006d..6da87f4e98 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx
@@ -3,7 +3,6 @@ import 'reactflow/dist/style.css';
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
import { Combobox, Flex, Popover, PopoverAnchor, PopoverBody, PopoverContent } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
-import { useAppToaster } from 'app/components/Toaster';
import { useAppDispatch, useAppStore } from 'app/store/storeHooks';
import type { SelectInstance } from 'chakra-react-select';
import { useBuildNode } from 'features/nodes/hooks/useBuildNode';
@@ -24,6 +23,7 @@ import { connectionToEdge } from 'features/nodes/store/util/reactFlowUtil';
import { validateConnectionTypes } from 'features/nodes/store/util/validateConnectionTypes';
import type { AnyNode } from 'features/nodes/types/invocation';
import { isInvocationNode } from 'features/nodes/types/invocation';
+import { toast } from 'features/toast/toast';
import { filter, map, memoize, some } from 'lodash-es';
import { memo, useCallback, useMemo, useRef } from 'react';
import { flushSync } from 'react-dom';
@@ -60,7 +60,6 @@ const filterOption = memoize((option: FilterOptionOption, inputV
const AddNodePopover = () => {
const dispatch = useAppDispatch();
const buildInvocation = useBuildNode();
- const toaster = useAppToaster();
const { t } = useTranslation();
const selectRef = useRef | null>(null);
const inputRef = useRef(null);
@@ -127,7 +126,7 @@ const AddNodePopover = () => {
const errorMessage = t('nodes.unknownNode', {
nodeType: nodeType,
});
- toaster({
+ toast({
status: 'error',
title: errorMessage,
});
@@ -163,7 +162,7 @@ const AddNodePopover = () => {
}
return node;
},
- [buildInvocation, store, dispatch, t, toaster]
+ [buildInvocation, store, dispatch, t]
);
const onChange = useCallback(
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx
index 9a675c7214..7ceb991bd8 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/ClearFlowButton.tsx
@@ -1,8 +1,7 @@
import { ConfirmationAlertDialog, Flex, IconButton, Text, useDisclosure } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiTrashSimpleFill } from 'react-icons/pi';
@@ -16,14 +15,11 @@ const ClearFlowButton = () => {
const handleNewWorkflow = useCallback(() => {
dispatch(nodeEditorReset());
- dispatch(
- addToast(
- makeToast({
- title: t('workflows.workflowCleared'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'WORKFLOW_CLEARED',
+ title: t('workflows.workflowCleared'),
+ status: 'success',
+ });
onClose();
}, [dispatch, onClose, t]);
diff --git a/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts b/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts
index d892906fcd..683f5479f9 100644
--- a/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts
+++ b/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts
@@ -1,10 +1,10 @@
import { skipToken } from '@reduxjs/toolkit/query';
-import { useAppToaster } from 'app/components/Toaster';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
import { parseAndRecallAllMetadata } from 'features/metadata/util/handlers';
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
+import { toast } from 'features/toast/toast';
import { setActiveTab } from 'features/ui/store/uiSlice';
import { t } from 'i18next';
import { useCallback, useEffect } from 'react';
@@ -16,7 +16,6 @@ export const usePreselectedImage = (selectedImage?: {
}) => {
const dispatch = useAppDispatch();
const optimalDimension = useAppSelector(selectOptimalDimension);
- const toaster = useAppToaster();
const { currentData: selectedImageDto } = useGetImageDTOQuery(selectedImage?.imageName ?? skipToken);
@@ -26,14 +25,13 @@ export const usePreselectedImage = (selectedImage?: {
if (selectedImageDto) {
dispatch(setInitialCanvasImage(selectedImageDto, optimalDimension));
dispatch(setActiveTab('canvas'));
- toaster({
+ toast({
+ id: 'SENT_TO_CANVAS',
title: t('toast.sentToUnifiedCanvas'),
status: 'info',
- duration: 2500,
- isClosable: true,
});
}
- }, [selectedImageDto, dispatch, optimalDimension, toaster]);
+ }, [selectedImageDto, dispatch, optimalDimension]);
const handleSendToImg2Img = useCallback(() => {
if (selectedImageDto) {
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts b/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts
index 8600525dae..9d92eabff8 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useCancelBatch.ts
@@ -1,5 +1,5 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useCancelByBatchIdsMutation, useGetBatchStatusQuery } from 'services/api/endpoints/queue';
@@ -23,7 +23,6 @@ export const useCancelBatch = (batch_id: string) => {
const [trigger, { isLoading }] = useCancelByBatchIdsMutation({
fixedCacheKey: 'cancelByBatchIds',
});
- const dispatch = useAppDispatch();
const { t } = useTranslation();
const cancelBatch = useCallback(async () => {
if (isCanceled) {
@@ -31,21 +30,19 @@ export const useCancelBatch = (batch_id: string) => {
}
try {
await trigger({ batch_ids: [batch_id] }).unwrap();
- dispatch(
- addToast({
- title: t('queue.cancelBatchSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'CANCEL_BATCH_SUCCEEDED',
+ title: t('queue.cancelBatchSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('queue.cancelBatchFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'CANCEL_BATCH_FAILED',
+ title: t('queue.cancelBatchFailed'),
+ status: 'error',
+ });
}
- }, [batch_id, dispatch, isCanceled, t, trigger]);
+ }, [batch_id, isCanceled, t, trigger]);
return { cancelBatch, isLoading, isCanceled, isDisabled: !isConnected };
};
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts b/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts
index a0275076e3..057490ed99 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useCancelCurrentQueueItem.ts
@@ -1,5 +1,5 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { isNil } from 'lodash-es';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -9,7 +9,6 @@ export const useCancelCurrentQueueItem = () => {
const isConnected = useAppSelector((s) => s.system.isConnected);
const { data: queueStatus } = useGetQueueStatusQuery();
const [trigger, { isLoading }] = useCancelQueueItemMutation();
- const dispatch = useAppDispatch();
const { t } = useTranslation();
const currentQueueItemId = useMemo(() => queueStatus?.queue.item_id, [queueStatus?.queue.item_id]);
const cancelQueueItem = useCallback(async () => {
@@ -18,21 +17,19 @@ export const useCancelCurrentQueueItem = () => {
}
try {
await trigger(currentQueueItemId).unwrap();
- dispatch(
- addToast({
- title: t('queue.cancelSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'QUEUE_CANCEL_SUCCEEDED',
+ title: t('queue.cancelSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('queue.cancelFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'QUEUE_CANCEL_FAILED',
+ title: t('queue.cancelFailed'),
+ status: 'error',
+ });
}
- }, [currentQueueItemId, dispatch, t, trigger]);
+ }, [currentQueueItemId, t, trigger]);
const isDisabled = useMemo(() => !isConnected || isNil(currentQueueItemId), [isConnected, currentQueueItemId]);
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts b/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts
index f22b98a7ee..268eca75cc 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useCancelQueueItem.ts
@@ -1,5 +1,5 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useCancelQueueItemMutation } from 'services/api/endpoints/queue';
@@ -7,26 +7,23 @@ import { useCancelQueueItemMutation } from 'services/api/endpoints/queue';
export const useCancelQueueItem = (item_id: number) => {
const isConnected = useAppSelector((s) => s.system.isConnected);
const [trigger, { isLoading }] = useCancelQueueItemMutation();
- const dispatch = useAppDispatch();
const { t } = useTranslation();
const cancelQueueItem = useCallback(async () => {
try {
await trigger(item_id).unwrap();
- dispatch(
- addToast({
- title: t('queue.cancelSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'QUEUE_CANCEL_SUCCEEDED',
+ title: t('queue.cancelSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('queue.cancelFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'QUEUE_CANCEL_FAILED',
+ title: t('queue.cancelFailed'),
+ status: 'error',
+ });
}
- }, [dispatch, item_id, t, trigger]);
+ }, [item_id, t, trigger]);
return { cancelQueueItem, isLoading, isDisabled: !isConnected };
};
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts b/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts
index 34b46d79b4..7ef9d93742 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useClearInvocationCache.ts
@@ -1,12 +1,11 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useClearInvocationCacheMutation, useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
export const useClearInvocationCache = () => {
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
const isConnected = useAppSelector((s) => s.system.isConnected);
const [trigger, { isLoading }] = useClearInvocationCacheMutation({
@@ -22,21 +21,19 @@ export const useClearInvocationCache = () => {
try {
await trigger().unwrap();
- dispatch(
- addToast({
- title: t('invocationCache.clearSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'INVOCATION_CACHE_CLEAR_SUCCEEDED',
+ title: t('invocationCache.clearSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('invocationCache.clearFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'INVOCATION_CACHE_CLEAR_FAILED',
+ title: t('invocationCache.clearFailed'),
+ status: 'error',
+ });
}
- }, [isDisabled, trigger, dispatch, t]);
+ }, [isDisabled, trigger, t]);
return { clearInvocationCache, isLoading, cacheStatus, isDisabled };
};
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts b/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts
index 0ca2528dab..ca7d1e4894 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useClearQueue.ts
@@ -1,6 +1,6 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { listCursorChanged, listPriorityChanged } from 'features/queue/store/queueSlice';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useClearQueueMutation, useGetQueueStatusQuery } from 'services/api/endpoints/queue';
@@ -21,21 +21,19 @@ export const useClearQueue = () => {
try {
await trigger().unwrap();
- dispatch(
- addToast({
- title: t('queue.clearSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'QUEUE_CLEAR_SUCCEEDED',
+ title: t('queue.clearSucceeded'),
+ status: 'success',
+ });
dispatch(listCursorChanged(undefined));
dispatch(listPriorityChanged(undefined));
} catch {
- dispatch(
- addToast({
- title: t('queue.clearFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'QUEUE_CLEAR_FAILED',
+ title: t('queue.clearFailed'),
+ status: 'error',
+ });
}
}, [queueStatus?.queue.total, trigger, dispatch, t]);
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts b/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts
index c7d5a575d2..371e9198e7 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useDisableInvocationCache.ts
@@ -1,12 +1,11 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useDisableInvocationCacheMutation, useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
export const useDisableInvocationCache = () => {
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
const isConnected = useAppSelector((s) => s.system.isConnected);
const [trigger, { isLoading }] = useDisableInvocationCacheMutation({
@@ -25,21 +24,19 @@ export const useDisableInvocationCache = () => {
try {
await trigger().unwrap();
- dispatch(
- addToast({
- title: t('invocationCache.disableSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'INVOCATION_CACHE_DISABLE_SUCCEEDED',
+ title: t('invocationCache.disableSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('invocationCache.disableFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'INVOCATION_CACHE_DISABLE_FAILED',
+ title: t('invocationCache.disableFailed'),
+ status: 'error',
+ });
}
- }, [isDisabled, trigger, dispatch, t]);
+ }, [isDisabled, trigger, t]);
return { disableInvocationCache, isLoading, cacheStatus, isDisabled };
};
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts b/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts
index 22bb7aa97d..fb39cf7347 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useEnableInvocationCache.ts
@@ -1,12 +1,11 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useEnableInvocationCacheMutation, useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
export const useEnableInvocationCache = () => {
const { t } = useTranslation();
- const dispatch = useAppDispatch();
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
const isConnected = useAppSelector((s) => s.system.isConnected);
const [trigger, { isLoading }] = useEnableInvocationCacheMutation({
@@ -25,21 +24,19 @@ export const useEnableInvocationCache = () => {
try {
await trigger().unwrap();
- dispatch(
- addToast({
- title: t('invocationCache.enableSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'INVOCATION_CACHE_ENABLE_SUCCEEDED',
+ title: t('invocationCache.enableSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('invocationCache.enableFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'INVOCATION_CACHE_ENABLE_FAILED',
+ title: t('invocationCache.enableFailed'),
+ status: 'error',
+ });
}
- }, [isDisabled, trigger, dispatch, t]);
+ }, [isDisabled, trigger, t]);
return { enableInvocationCache, isLoading, cacheStatus, isDisabled };
};
diff --git a/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts b/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts
index 25c3423bcf..f5424c6b18 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/usePauseProcessor.ts
@@ -1,11 +1,10 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetQueueStatusQuery, usePauseProcessorMutation } from 'services/api/endpoints/queue';
export const usePauseProcessor = () => {
- const dispatch = useAppDispatch();
const { t } = useTranslation();
const isConnected = useAppSelector((s) => s.system.isConnected);
const { data: queueStatus } = useGetQueueStatusQuery();
@@ -21,21 +20,19 @@ export const usePauseProcessor = () => {
}
try {
await trigger().unwrap();
- dispatch(
- addToast({
- title: t('queue.pauseSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'PAUSE_SUCCEEDED',
+ title: t('queue.pauseSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('queue.pauseFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PAUSE_FAILED',
+ title: t('queue.pauseFailed'),
+ status: 'error',
+ });
}
- }, [isStarted, trigger, dispatch, t]);
+ }, [isStarted, trigger, t]);
const isDisabled = useMemo(() => !isConnected || !isStarted, [isConnected, isStarted]);
diff --git a/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts b/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts
index 2cfec364fa..eaeabe5423 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/usePruneQueue.ts
@@ -1,6 +1,6 @@
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { listCursorChanged, listPriorityChanged } from 'features/queue/store/queueSlice';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetQueueStatusQuery, usePruneQueueMutation } from 'services/api/endpoints/queue';
@@ -30,21 +30,19 @@ export const usePruneQueue = () => {
}
try {
const data = await trigger().unwrap();
- dispatch(
- addToast({
- title: t('queue.pruneSucceeded', { item_count: data.deleted }),
- status: 'success',
- })
- );
+ toast({
+ id: 'PRUNE_SUCCEEDED',
+ title: t('queue.pruneSucceeded', { item_count: data.deleted }),
+ status: 'success',
+ });
dispatch(listCursorChanged(undefined));
dispatch(listPriorityChanged(undefined));
} catch {
- dispatch(
- addToast({
- title: t('queue.pruneFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PRUNE_FAILED',
+ title: t('queue.pruneFailed'),
+ status: 'error',
+ });
}
}, [finishedCount, trigger, dispatch, t]);
diff --git a/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts b/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts
index 6e3ea83d7d..851b268416 100644
--- a/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts
+++ b/invokeai/frontend/web/src/features/queue/hooks/useResumeProcessor.ts
@@ -1,11 +1,10 @@
-import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import { addToast } from 'features/system/store/systemSlice';
+import { useAppSelector } from 'app/store/storeHooks';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGetQueueStatusQuery, useResumeProcessorMutation } from 'services/api/endpoints/queue';
export const useResumeProcessor = () => {
- const dispatch = useAppDispatch();
const isConnected = useAppSelector((s) => s.system.isConnected);
const { data: queueStatus } = useGetQueueStatusQuery();
const { t } = useTranslation();
@@ -21,21 +20,19 @@ export const useResumeProcessor = () => {
}
try {
await trigger().unwrap();
- dispatch(
- addToast({
- title: t('queue.resumeSucceeded'),
- status: 'success',
- })
- );
+ toast({
+ id: 'PROCESSOR_RESUMED',
+ title: t('queue.resumeSucceeded'),
+ status: 'success',
+ });
} catch {
- dispatch(
- addToast({
- title: t('queue.resumeFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'PROCESSOR_RESUME_FAILED',
+ title: t('queue.resumeFailed'),
+ status: 'error',
+ });
}
- }, [isStarted, trigger, dispatch, t]);
+ }, [isStarted, trigger, t]);
const isDisabled = useMemo(() => !isConnected || isStarted, [isConnected, isStarted]);
diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts b/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts
index e9f1debcf8..f392acb521 100644
--- a/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts
@@ -1,7 +1,7 @@
import { useAppDispatch } from 'app/store/storeHooks';
import { resetCanvas } from 'features/canvas/store/canvasSlice';
import { controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useClearIntermediatesMutation, useGetIntermediatesCountQuery } from 'services/api/endpoints/images';
@@ -42,20 +42,18 @@ export const useClearIntermediates = (shouldShowClearIntermediates: boolean): Us
.then((clearedCount) => {
dispatch(controlAdaptersReset());
dispatch(resetCanvas());
- dispatch(
- addToast({
- title: t('settings.intermediatesCleared', { count: clearedCount }),
- status: 'info',
- })
- );
+ toast({
+ id: 'INTERMEDIATES_CLEARED',
+ title: t('settings.intermediatesCleared', { count: clearedCount }),
+ status: 'info',
+ });
})
.catch(() => {
- dispatch(
- addToast({
- title: t('settings.intermediatesClearedFailed'),
- status: 'error',
- })
- );
+ toast({
+ id: 'INTERMEDIATES_CLEAR_FAILED',
+ title: t('settings.intermediatesClearedFailed'),
+ status: 'error',
+ });
});
}, [t, _clearIntermediates, dispatch, hasPendingItems]);
diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts
index 17ddec5471..65903460ed 100644
--- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts
+++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts
@@ -1,11 +1,7 @@
-import type { UseToastOptions } from '@invoke-ai/ui-library';
import type { PayloadAction } from '@reduxjs/toolkit';
-import { createSlice, isAnyOf } from '@reduxjs/toolkit';
+import { createSlice } from '@reduxjs/toolkit';
import type { PersistConfig, RootState } from 'app/store/store';
import { calculateStepPercentage } from 'features/system/util/calculateStepPercentage';
-import { makeToast } from 'features/system/util/makeToast';
-import { t } from 'i18next';
-import { startCase } from 'lodash-es';
import type { LogLevelName } from 'roarr';
import {
socketConnected,
@@ -13,13 +9,10 @@ import {
socketGeneratorProgress,
socketGraphExecutionStateComplete,
socketInvocationComplete,
- socketInvocationError,
- socketInvocationRetrievalError,
socketInvocationStarted,
socketModelLoadCompleted,
socketModelLoadStarted,
socketQueueItemStatusChanged,
- socketSessionRetrievalError,
} from 'services/events/actions';
import type { Language, SystemState } from './types';
@@ -29,7 +22,6 @@ const initialSystemState: SystemState = {
isConnected: false,
shouldConfirmOnDelete: true,
enableImageDebugging: false,
- toastQueue: [],
denoiseProgress: null,
shouldAntialiasProgressImage: false,
consoleLogLevel: 'debug',
@@ -51,12 +43,6 @@ export const systemSlice = createSlice({
setEnableImageDebugging: (state, action: PayloadAction) => {
state.enableImageDebugging = action.payload;
},
- addToast: (state, action: PayloadAction) => {
- state.toastQueue.push(action.payload);
- },
- clearToastQueue: (state) => {
- state.toastQueue = [];
- },
consoleLogLevelChanged: (state, action: PayloadAction) => {
state.consoleLogLevel = action.payload;
},
@@ -162,29 +148,12 @@ export const systemSlice = createSlice({
state.denoiseProgress = null;
}
});
-
- // *** Matchers - must be after all cases ***
-
- /**
- * Any server error
- */
- builder.addMatcher(isAnyServerError, (state, action) => {
- state.toastQueue.push(
- makeToast({
- title: t('toast.serverError'),
- status: 'error',
- description: startCase(action.payload.data.error_type),
- })
- );
- });
},
});
export const {
setShouldConfirmOnDelete,
setEnableImageDebugging,
- addToast,
- clearToastQueue,
consoleLogLevelChanged,
shouldLogToConsoleChanged,
shouldAntialiasProgressImageChanged,
@@ -194,8 +163,6 @@ export const {
setShouldEnableInformationalPopovers,
} = systemSlice.actions;
-const isAnyServerError = isAnyOf(socketInvocationError, socketSessionRetrievalError, socketInvocationRetrievalError);
-
export const selectSystemSlice = (state: RootState) => state.system;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
diff --git a/invokeai/frontend/web/src/features/system/store/types.ts b/invokeai/frontend/web/src/features/system/store/types.ts
index 430df9aa7d..d8bc8cd08a 100644
--- a/invokeai/frontend/web/src/features/system/store/types.ts
+++ b/invokeai/frontend/web/src/features/system/store/types.ts
@@ -1,4 +1,3 @@
-import type { UseToastOptions } from '@invoke-ai/ui-library';
import type { LogLevel } from 'app/logging/logger';
import type { ProgressImage } from 'services/events/types';
import { z } from 'zod';
@@ -47,7 +46,6 @@ export interface SystemState {
isConnected: boolean;
shouldConfirmOnDelete: boolean;
enableImageDebugging: boolean;
- toastQueue: UseToastOptions[];
denoiseProgress: DenoiseProgress | null;
consoleLogLevel: LogLevel;
shouldLogToConsole: boolean;
diff --git a/invokeai/frontend/web/src/features/system/util/makeToast.ts b/invokeai/frontend/web/src/features/system/util/makeToast.ts
deleted file mode 100644
index aa77fd60ae..0000000000
--- a/invokeai/frontend/web/src/features/system/util/makeToast.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import type { UseToastOptions } from '@invoke-ai/ui-library';
-
-export type MakeToastArg = string | UseToastOptions;
-
-/**
- * Makes a toast from a string or a UseToastOptions object.
- * If a string is passed, the toast will have the status 'info' and will be closable with a duration of 2500ms.
- */
-export const makeToast = (arg: MakeToastArg): UseToastOptions => {
- if (typeof arg === 'string') {
- return {
- title: arg,
- status: 'info',
- isClosable: true,
- duration: 2500,
- };
- }
-
- return { status: 'info', isClosable: true, duration: 2500, ...arg };
-};
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx
index b01d259da7..701441b093 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx
+++ b/invokeai/frontend/web/src/features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog.tsx
@@ -2,8 +2,7 @@ import { ConfirmationAlertDialog, Flex, Text, useDisclosure } from '@invoke-ai/u
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
import { workflowModeChanged } from 'features/nodes/store/workflowSlice';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -21,14 +20,11 @@ export const NewWorkflowConfirmationAlertDialog = memo((props: Props) => {
dispatch(nodeEditorReset());
dispatch(workflowModeChanged('edit'));
- dispatch(
- addToast(
- makeToast({
- title: t('workflows.newWorkflowCreated'),
- status: 'success',
- })
- )
- );
+ toast({
+ id: 'NEW_WORKFLOW_CREATED',
+ title: t('workflows.newWorkflowCreated'),
+ status: 'success',
+ });
onClose();
}, [dispatch, onClose, t]);
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDeleteLibraryWorkflow.ts b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDeleteLibraryWorkflow.ts
index 7d2c636e9c..7b93a34f83 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDeleteLibraryWorkflow.ts
+++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useDeleteLibraryWorkflow.ts
@@ -1,5 +1,4 @@
-import { useToast } from '@invoke-ai/ui-library';
-import { useAppToaster } from 'app/components/Toaster';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useDeleteWorkflowMutation, workflowsApi } from 'services/api/endpoints/workflows';
@@ -17,8 +16,6 @@ type UseDeleteLibraryWorkflowReturn = {
type UseDeleteLibraryWorkflow = (arg: UseDeleteLibraryWorkflowOptions) => UseDeleteLibraryWorkflowReturn;
export const useDeleteLibraryWorkflow: UseDeleteLibraryWorkflow = ({ onSuccess, onError }) => {
- const toaster = useAppToaster();
- const toast = useToast();
const { t } = useTranslation();
const [_deleteWorkflow, deleteWorkflowResult] = useDeleteWorkflowMutation();
@@ -26,21 +23,21 @@ export const useDeleteLibraryWorkflow: UseDeleteLibraryWorkflow = ({ onSuccess,
async (workflow_id: string) => {
try {
await _deleteWorkflow(workflow_id).unwrap();
- toaster({
+ toast({
+ id: 'WORKFLOW_DELETED',
title: t('toast.workflowDeleted'),
});
onSuccess && onSuccess();
} catch {
- if (!toast.isActive(`auth-error-toast-${workflowsApi.endpoints.deleteWorkflow.name}`)) {
- toaster({
- title: t('toast.problemDeletingWorkflow'),
- status: 'error',
- });
- }
+ toast({
+ id: `AUTH_ERROR_TOAST_${workflowsApi.endpoints.deleteWorkflow.name}`,
+ title: t('toast.problemDeletingWorkflow'),
+ status: 'error',
+ });
onError && onError();
}
},
- [_deleteWorkflow, toaster, t, onSuccess, onError, toast]
+ [_deleteWorkflow, t, onSuccess, onError]
);
return { deleteWorkflow, deleteWorkflowResult };
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts
index 7ea9329540..12c302f9c9 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts
+++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts
@@ -1,6 +1,6 @@
-import { useAppToaster } from 'app/components/Toaster';
import { useAppDispatch } from 'app/store/storeHooks';
import { workflowLoadRequested } from 'features/nodes/store/actions';
+import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyGetImageWorkflowQuery } from 'services/api/endpoints/images';
@@ -21,7 +21,6 @@ type UseGetAndLoadEmbeddedWorkflow = (
export const useGetAndLoadEmbeddedWorkflow: UseGetAndLoadEmbeddedWorkflow = ({ onSuccess, onError }) => {
const dispatch = useAppDispatch();
- const toaster = useAppToaster();
const { t } = useTranslation();
const [_getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult] = useLazyGetImageWorkflowQuery();
const getAndLoadEmbeddedWorkflow = useCallback(
@@ -33,20 +32,22 @@ export const useGetAndLoadEmbeddedWorkflow: UseGetAndLoadEmbeddedWorkflow = ({ o
// No toast - the listener for this action does that after the workflow is loaded
onSuccess && onSuccess();
} else {
- toaster({
+ toast({
+ id: 'PROBLEM_RETRIEVING_WORKFLOW',
title: t('toast.problemRetrievingWorkflow'),
status: 'error',
});
}
} catch {
- toaster({
+ toast({
+ id: 'PROBLEM_RETRIEVING_WORKFLOW',
title: t('toast.problemRetrievingWorkflow'),
status: 'error',
});
onError && onError();
}
},
- [_getAndLoadEmbeddedWorkflow, dispatch, onSuccess, toaster, t, onError]
+ [_getAndLoadEmbeddedWorkflow, dispatch, onSuccess, t, onError]
);
return { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult };
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts
index f616812175..89933999bd 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts
+++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts
@@ -1,5 +1,4 @@
import { useToast } from '@invoke-ai/ui-library';
-import { useAppToaster } from 'app/components/Toaster';
import { useAppDispatch } from 'app/store/storeHooks';
import { workflowLoadRequested } from 'features/nodes/store/actions';
import { useCallback } from 'react';
@@ -20,7 +19,6 @@ type UseGetAndLoadLibraryWorkflow = (arg: UseGetAndLoadLibraryWorkflowOptions) =
export const useGetAndLoadLibraryWorkflow: UseGetAndLoadLibraryWorkflow = ({ onSuccess, onError }) => {
const dispatch = useAppDispatch();
- const toaster = useAppToaster();
const toast = useToast();
const { t } = useTranslation();
const [_getAndLoadWorkflow, getAndLoadWorkflowResult] = useLazyGetWorkflowQuery();
@@ -33,16 +31,15 @@ export const useGetAndLoadLibraryWorkflow: UseGetAndLoadLibraryWorkflow = ({ onS
// No toast - the listener for this action does that after the workflow is loaded
onSuccess && onSuccess();
} catch {
- if (!toast.isActive(`auth-error-toast-${workflowsApi.endpoints.getWorkflow.name}`)) {
- toaster({
- title: t('toast.problemRetrievingWorkflow'),
- status: 'error',
- });
- }
+ toast({
+ id: `AUTH_ERROR_TOAST_${workflowsApi.endpoints.getWorkflow.name}`,
+ title: t('toast.problemRetrievingWorkflow'),
+ status: 'error',
+ });
onError && onError();
}
},
- [_getAndLoadWorkflow, dispatch, onSuccess, toaster, t, onError, toast]
+ [_getAndLoadWorkflow, dispatch, onSuccess, t, onError, toast]
);
return { getAndLoadWorkflow, getAndLoadWorkflowResult };
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx
index 7a39d4ecd0..94a1ef5c51 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx
+++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx
@@ -1,8 +1,7 @@
import { useLogger } from 'app/logging/useLogger';
import { useAppDispatch } from 'app/store/storeHooks';
import { workflowLoadRequested } from 'features/nodes/store/actions';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import { workflowLoadedFromFile } from 'features/workflowLibrary/store/actions';
import type { RefObject } from 'react';
import { useCallback } from 'react';
@@ -35,14 +34,11 @@ export const useLoadWorkflowFromFile: UseLoadWorkflowFromFile = ({ resetRef, onS
} catch (e) {
// There was a problem reading the file
logger.error(t('nodes.unableToLoadWorkflow'));
- dispatch(
- addToast(
- makeToast({
- title: t('nodes.unableToLoadWorkflow'),
- status: 'error',
- })
- )
- );
+ toast({
+ id: 'UNABLE_TO_LOAD_WORKFLOW',
+ title: t('nodes.unableToLoadWorkflow'),
+ status: 'error',
+ });
reader.abort();
}
};
diff --git a/invokeai/frontend/web/src/services/api/authToastMiddleware.ts b/invokeai/frontend/web/src/services/api/authToastMiddleware.ts
index 002cc96174..3a906a613b 100644
--- a/invokeai/frontend/web/src/services/api/authToastMiddleware.ts
+++ b/invokeai/frontend/web/src/services/api/authToastMiddleware.ts
@@ -1,6 +1,6 @@
-import type { Middleware, MiddlewareAPI } from '@reduxjs/toolkit';
+import type { Middleware } from '@reduxjs/toolkit';
import { isRejectedWithValue } from '@reduxjs/toolkit';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { z } from 'zod';
@@ -22,7 +22,7 @@ const zRejectedForbiddenAction = z.object({
.optional(),
});
-export const authToastMiddleware: Middleware = (api: MiddlewareAPI) => (next) => (action) => {
+export const authToastMiddleware: Middleware = () => (next) => (action) => {
if (isRejectedWithValue(action)) {
try {
const parsed = zRejectedForbiddenAction.parse(action);
@@ -32,16 +32,13 @@ export const authToastMiddleware: Middleware = (api: MiddlewareAPI) => (next) =>
return;
}
- const { dispatch } = api;
const customMessage = parsed.payload.data.detail !== 'Forbidden' ? parsed.payload.data.detail : undefined;
- dispatch(
- addToast({
- id: `auth-error-toast-${endpointName}`,
- title: t('common.somethingWentWrong'),
- status: 'error',
- description: customMessage,
- })
- );
+ toast({
+ id: `auth-error-toast-${endpointName}`,
+ title: t('toast.somethingWentWrong'),
+ status: 'error',
+ description: customMessage,
+ });
} catch (error) {
// no-op
}
diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts
index 14edf6fb87..5d56a61add 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/images.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts
@@ -4,7 +4,7 @@ import { getStore } from 'app/store/nanostores/store';
import type { JSONObject } from 'common/types';
import type { BoardId } from 'features/gallery/store/types';
import { ASSETS_CATEGORIES, IMAGE_CATEGORIES, IMAGE_LIMIT } from 'features/gallery/store/types';
-import { addToast } from 'features/system/store/systemSlice';
+import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { keyBy } from 'lodash-es';
import type { components, paths } from 'services/api/schema';
@@ -206,13 +206,12 @@ export const imagesApi = api.injectEndpoints({
const { data } = await queryFulfilled;
if (data.deleted_images.length < imageDTOs.length) {
- dispatch(
- addToast({
- title: t('gallery.problemDeletingImages'),
- description: t('gallery.problemDeletingImagesDesc'),
- status: 'warning',
- })
- );
+ toast({
+ id: 'problem-deleting-images',
+ title: t('gallery.problemDeletingImages'),
+ description: t('gallery.problemDeletingImagesDesc'),
+ status: 'warning',
+ });
}
// convert to an object so we can access the successfully delete image DTOs by name
diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts
index 1160a2bee5..578b60efa7 100644
--- a/invokeai/frontend/web/src/services/api/types.ts
+++ b/invokeai/frontend/web/src/services/api/types.ts
@@ -1,4 +1,3 @@
-import type { UseToastOptions } from '@invoke-ai/ui-library';
import type { EntityState } from '@reduxjs/toolkit';
import type { components, paths } from 'services/api/schema';
import type { O } from 'ts-toolbelt';
@@ -200,7 +199,7 @@ type CanvasInitialImageAction = {
type ToastAction = {
type: 'TOAST';
- toastOptions?: UseToastOptions;
+ title?: string;
};
type AddToBatchAction = {
diff --git a/invokeai/frontend/web/src/services/events/util/setEventListeners.ts b/invokeai/frontend/web/src/services/events/util/setEventListeners.ts
index 4476624e4e..446a3e8a4b 100644
--- a/invokeai/frontend/web/src/services/events/util/setEventListeners.ts
+++ b/invokeai/frontend/web/src/services/events/util/setEventListeners.ts
@@ -2,8 +2,7 @@ import { $baseUrl } from 'app/store/nanostores/baseUrl';
import { $bulkDownloadId } from 'app/store/nanostores/bulkDownloadId';
import { $queueId } from 'app/store/nanostores/queueId';
import type { AppDispatch } from 'app/store/store';
-import { addToast } from 'features/system/store/systemSlice';
-import { makeToast } from 'features/system/util/makeToast';
+import { toast } from 'features/toast/toast';
import {
socketBulkDownloadCompleted,
socketBulkDownloadFailed,
@@ -52,15 +51,12 @@ export const setEventListeners = (arg: SetEventListenersArg) => {
if (error && error.message) {
const data: string | undefined = (error as unknown as { data: string | undefined }).data;
if (data === 'ERR_UNAUTHENTICATED') {
- dispatch(
- addToast(
- makeToast({
- title: error.message,
- status: 'error',
- duration: 10000,
- })
- )
- );
+ toast({
+ id: `connect-error-${error.message}`,
+ title: error.message,
+ status: 'error',
+ duration: 10000,
+ });
}
}
});