mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): custom error toast support (#8001)
* support for custom error toast components, starting with usage limit * add support for all usage limits --------- Co-authored-by: Mary Hipp <maryhipp@Marys-MacBook-Air.local>
This commit is contained in:
@@ -18,6 +18,7 @@ import { $openAPISchemaUrl } from 'app/store/nanostores/openAPISchemaUrl';
|
||||
import { $projectId, $projectName, $projectUrl } from 'app/store/nanostores/projectId';
|
||||
import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId';
|
||||
import { $store } from 'app/store/nanostores/store';
|
||||
import { $toastMap } from 'app/store/nanostores/toastMap';
|
||||
import { $whatsNew } from 'app/store/nanostores/whatsNew';
|
||||
import { createStore } from 'app/store/store';
|
||||
import type { PartialAppConfig } from 'app/types/invokeai';
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
DEFAULT_WORKFLOW_LIBRARY_TAG_CATEGORIES,
|
||||
} from 'features/nodes/store/workflowLibrarySlice';
|
||||
import type { WorkflowCategory } from 'features/nodes/types/workflow';
|
||||
import type { ToastConfig } from 'features/toast/toast';
|
||||
import type { PropsWithChildren, ReactNode } from 'react';
|
||||
import React, { lazy, memo, useEffect, useLayoutEffect, useMemo } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
@@ -59,6 +61,7 @@ interface Props extends PropsWithChildren {
|
||||
socketOptions?: Partial<ManagerOptions & SocketOptions>;
|
||||
isDebugging?: boolean;
|
||||
logo?: ReactNode;
|
||||
toastMap?: Record<string, ToastConfig>;
|
||||
whatsNew?: ReactNode[];
|
||||
workflowCategories?: WorkflowCategory[];
|
||||
workflowTagCategories?: WorkflowTagCategory[];
|
||||
@@ -87,6 +90,7 @@ const InvokeAIUI = ({
|
||||
socketOptions,
|
||||
isDebugging = false,
|
||||
logo,
|
||||
toastMap,
|
||||
workflowCategories,
|
||||
workflowTagCategories,
|
||||
workflowSortOptions,
|
||||
@@ -227,6 +231,16 @@ const InvokeAIUI = ({
|
||||
};
|
||||
}, [logo]);
|
||||
|
||||
useEffect(() => {
|
||||
if (toastMap) {
|
||||
$toastMap.set(toastMap);
|
||||
}
|
||||
|
||||
return () => {
|
||||
$toastMap.set(undefined);
|
||||
};
|
||||
}, [toastMap]);
|
||||
|
||||
useEffect(() => {
|
||||
if (whatsNew) {
|
||||
$whatsNew.set(whatsNew);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { ToastConfig } from 'features/toast/toast';
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const $toastMap = atom<Record<string, ToastConfig> | undefined>(undefined);
|
||||
@@ -9,7 +9,7 @@ export const toastApi = createStandaloneToast({
|
||||
}).toast;
|
||||
|
||||
// Slightly modified version of UseToastOptions
|
||||
type ToastConfig = Omit<UseToastOptions, 'id'> & {
|
||||
export type ToastConfig = Omit<UseToastOptions, 'id'> & {
|
||||
// Only string - Chakra allows numbers
|
||||
id?: string;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,35 @@
|
||||
import type { Middleware } from '@reduxjs/toolkit';
|
||||
import { isRejectedWithValue } from '@reduxjs/toolkit';
|
||||
import { $toastMap } from 'app/store/nanostores/toastMap';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
import { z } from 'zod';
|
||||
|
||||
const trialUsageErrorSubstring = 'usage allotment for the free trial';
|
||||
const trialUsageErrorCode = 'USAGE_LIMIT_TRIAL';
|
||||
|
||||
const orgUsageErrorSubstring = 'organization has reached its predefined usage allotment';
|
||||
const orgUsageErrorCode = 'USAGE_LIMIT_ORG';
|
||||
|
||||
const indieUsageErrorSubstring = 'usage allotment';
|
||||
const indieUsageErrorCode = 'USAGE_LIMIT_INDIE';
|
||||
|
||||
//TODO make this dynamic with returned error codes instead of substring check
|
||||
const getErrorCode = (errorString?: string) => {
|
||||
if (!errorString) {
|
||||
return undefined;
|
||||
}
|
||||
if (errorString.includes(trialUsageErrorSubstring)) {
|
||||
return trialUsageErrorCode;
|
||||
}
|
||||
if (errorString.includes(orgUsageErrorSubstring)) {
|
||||
return orgUsageErrorCode;
|
||||
}
|
||||
if (errorString.includes(indieUsageErrorSubstring)) {
|
||||
return indieUsageErrorCode;
|
||||
}
|
||||
};
|
||||
|
||||
const zRejectedForbiddenAction = z.object({
|
||||
payload: z.object({
|
||||
status: z.literal(403),
|
||||
@@ -31,14 +57,21 @@ export const authToastMiddleware: Middleware = () => (next) => (action) => {
|
||||
// do not show toast if problem is image access
|
||||
return next(action);
|
||||
}
|
||||
|
||||
const toastMap = $toastMap.get();
|
||||
const customMessage = parsed.payload.data.detail !== 'Forbidden' ? parsed.payload.data.detail : undefined;
|
||||
toast({
|
||||
id: `auth-error-toast-${endpointName}`,
|
||||
title: t('toast.somethingWentWrong'),
|
||||
status: 'error',
|
||||
description: customMessage,
|
||||
});
|
||||
const errorCode = getErrorCode(customMessage);
|
||||
const customToastConfig = errorCode ? toastMap?.[errorCode] : undefined;
|
||||
|
||||
if (customToastConfig) {
|
||||
toast(customToastConfig);
|
||||
} else {
|
||||
toast({
|
||||
id: `auth-error-toast-${endpointName}`,
|
||||
title: t('toast.somethingWentWrong'),
|
||||
status: 'error',
|
||||
description: customMessage,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user