Skip to content

Commit 954fce3

Browse files
maryhippMary Hipp
and
Mary Hipp
authored
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>
1 parent 8218891 commit 954fce3

File tree

4 files changed

+59
-8
lines changed

4 files changed

+59
-8
lines changed

invokeai/frontend/web/src/app/components/InvokeAIUI.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { $openAPISchemaUrl } from 'app/store/nanostores/openAPISchemaUrl';
1818
import { $projectId, $projectName, $projectUrl } from 'app/store/nanostores/projectId';
1919
import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId';
2020
import { $store } from 'app/store/nanostores/store';
21+
import { $toastMap } from 'app/store/nanostores/toastMap';
2122
import { $whatsNew } from 'app/store/nanostores/whatsNew';
2223
import { createStore } from 'app/store/store';
2324
import type { PartialAppConfig } from 'app/types/invokeai';
@@ -32,6 +33,7 @@ import {
3233
DEFAULT_WORKFLOW_LIBRARY_TAG_CATEGORIES,
3334
} from 'features/nodes/store/workflowLibrarySlice';
3435
import type { WorkflowCategory } from 'features/nodes/types/workflow';
36+
import type { ToastConfig } from 'features/toast/toast';
3537
import type { PropsWithChildren, ReactNode } from 'react';
3638
import React, { lazy, memo, useEffect, useLayoutEffect, useMemo } from 'react';
3739
import { Provider } from 'react-redux';
@@ -59,6 +61,7 @@ interface Props extends PropsWithChildren {
5961
socketOptions?: Partial<ManagerOptions & SocketOptions>;
6062
isDebugging?: boolean;
6163
logo?: ReactNode;
64+
toastMap?: Record<string, ToastConfig>;
6265
whatsNew?: ReactNode[];
6366
workflowCategories?: WorkflowCategory[];
6467
workflowTagCategories?: WorkflowTagCategory[];
@@ -87,6 +90,7 @@ const InvokeAIUI = ({
8790
socketOptions,
8891
isDebugging = false,
8992
logo,
93+
toastMap,
9094
workflowCategories,
9195
workflowTagCategories,
9296
workflowSortOptions,
@@ -227,6 +231,16 @@ const InvokeAIUI = ({
227231
};
228232
}, [logo]);
229233

234+
useEffect(() => {
235+
if (toastMap) {
236+
$toastMap.set(toastMap);
237+
}
238+
239+
return () => {
240+
$toastMap.set(undefined);
241+
};
242+
}, [toastMap]);
243+
230244
useEffect(() => {
231245
if (whatsNew) {
232246
$whatsNew.set(whatsNew);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { ToastConfig } from 'features/toast/toast';
2+
import { atom } from 'nanostores';
3+
4+
export const $toastMap = atom<Record<string, ToastConfig> | undefined>(undefined);

invokeai/frontend/web/src/features/toast/toast.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const toastApi = createStandaloneToast({
99
}).toast;
1010

1111
// Slightly modified version of UseToastOptions
12-
type ToastConfig = Omit<UseToastOptions, 'id'> & {
12+
export type ToastConfig = Omit<UseToastOptions, 'id'> & {
1313
// Only string - Chakra allows numbers
1414
id?: string;
1515
};

invokeai/frontend/web/src/services/api/authToastMiddleware.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
11
import type { Middleware } from '@reduxjs/toolkit';
22
import { isRejectedWithValue } from '@reduxjs/toolkit';
3+
import { $toastMap } from 'app/store/nanostores/toastMap';
34
import { toast } from 'features/toast/toast';
45
import { t } from 'i18next';
56
import { z } from 'zod';
67

8+
const trialUsageErrorSubstring = 'usage allotment for the free trial';
9+
const trialUsageErrorCode = 'USAGE_LIMIT_TRIAL';
10+
11+
const orgUsageErrorSubstring = 'organization has reached its predefined usage allotment';
12+
const orgUsageErrorCode = 'USAGE_LIMIT_ORG';
13+
14+
const indieUsageErrorSubstring = 'usage allotment';
15+
const indieUsageErrorCode = 'USAGE_LIMIT_INDIE';
16+
17+
//TODO make this dynamic with returned error codes instead of substring check
18+
const getErrorCode = (errorString?: string) => {
19+
if (!errorString) {
20+
return undefined;
21+
}
22+
if (errorString.includes(trialUsageErrorSubstring)) {
23+
return trialUsageErrorCode;
24+
}
25+
if (errorString.includes(orgUsageErrorSubstring)) {
26+
return orgUsageErrorCode;
27+
}
28+
if (errorString.includes(indieUsageErrorSubstring)) {
29+
return indieUsageErrorCode;
30+
}
31+
};
32+
733
const zRejectedForbiddenAction = z.object({
834
payload: z.object({
935
status: z.literal(403),
@@ -31,14 +57,21 @@ export const authToastMiddleware: Middleware = () => (next) => (action) => {
3157
// do not show toast if problem is image access
3258
return next(action);
3359
}
34-
60+
const toastMap = $toastMap.get();
3561
const customMessage = parsed.payload.data.detail !== 'Forbidden' ? parsed.payload.data.detail : undefined;
36-
toast({
37-
id: `auth-error-toast-${endpointName}`,
38-
title: t('toast.somethingWentWrong'),
39-
status: 'error',
40-
description: customMessage,
41-
});
62+
const errorCode = getErrorCode(customMessage);
63+
const customToastConfig = errorCode ? toastMap?.[errorCode] : undefined;
64+
65+
if (customToastConfig) {
66+
toast(customToastConfig);
67+
} else {
68+
toast({
69+
id: `auth-error-toast-${endpointName}`,
70+
title: t('toast.somethingWentWrong'),
71+
status: 'error',
72+
description: customMessage,
73+
});
74+
}
4275
} catch (error) {
4376
// no-op
4477
}

0 commit comments

Comments
 (0)