workflow library updates (#5568)

* dont show duplicate toasts if workflow actions fail due to auth

* dynamic order by options based on projectId

* add endpointName to authtoast to makeit unique per endpoint

* lint

* update toast logic to check based on endpoint name w type safety

* fix save as endpoit name

* lint

* fix type

---------

Co-authored-by: Mary Hipp <maryhipp@Marys-MacBook-Air.local>
This commit is contained in:
Mary Hipp Rogers 2024-01-25 11:43:47 -05:00 committed by GitHub
parent 57dafd294d
commit 89da976949
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 87 additions and 32 deletions

View File

@ -61,13 +61,21 @@ const WorkflowLibraryList = () => {
const [category, setCategory] = useState<WorkflowCategory>('user');
const [page, setPage] = useState(0);
const [query, setQuery] = useState('');
const [order_by, setOrderBy] = useState<WorkflowRecordOrderBy>('opened_at');
const projectId = useStore($projectId);
const orderByOptions = useMemo(() => {
return projectId
? ORDER_BY_OPTIONS.filter((option) => option.value !== 'opened_at')
: ORDER_BY_OPTIONS;
}, [projectId]);
const [order_by, setOrderBy] = useState<WorkflowRecordOrderBy>(
orderByOptions[0]?.value as WorkflowRecordOrderBy
);
const [direction, setDirection] = useState<SQLiteDirection>('ASC');
const [debouncedQuery] = useDebounce(query, 500);
const projectId = useStore($projectId);
const queryArg = useMemo<Parameters<typeof useListWorkflowsQuery>[0]>(() => {
if (category === 'user') {
if (category !== 'default') {
return {
page,
per_page: PER_PAGE,
@ -101,8 +109,8 @@ const WorkflowLibraryList = () => {
[order_by]
);
const valueOrderBy = useMemo(
() => ORDER_BY_OPTIONS.find((o) => o.value === order_by),
[order_by]
() => orderByOptions.find((o) => o.value === order_by),
[order_by, orderByOptions]
);
const onChangeDirection = useCallback<ComboboxOnChange>(
@ -186,7 +194,7 @@ const WorkflowLibraryList = () => {
<FormLabel>{t('common.orderBy')}</FormLabel>
<Combobox
value={valueOrderBy}
options={ORDER_BY_OPTIONS}
options={orderByOptions}
onChange={onChangeOrderBy}
/>
</FormControl>

View File

@ -1,7 +1,11 @@
import { useToast } from '@invoke-ai/ui';
import { useAppToaster } from 'app/components/Toaster';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useDeleteWorkflowMutation } from 'services/api/endpoints/workflows';
import {
useDeleteWorkflowMutation,
workflowsApi,
} from 'services/api/endpoints/workflows';
type UseDeleteLibraryWorkflowOptions = {
onSuccess?: () => void;
@ -22,6 +26,7 @@ export const useDeleteLibraryWorkflow: UseDeleteLibraryWorkflow = ({
onError,
}) => {
const toaster = useAppToaster();
const toast = useToast();
const { t } = useTranslation();
const [_deleteWorkflow, deleteWorkflowResult] = useDeleteWorkflowMutation();
@ -34,14 +39,20 @@ export const useDeleteLibraryWorkflow: UseDeleteLibraryWorkflow = ({
});
onSuccess && onSuccess();
} catch {
toaster({
title: t('toast.problemDeletingWorkflow'),
status: 'error',
});
if (
!toast.isActive(
`auth-error-toast-${workflowsApi.endpoints.deleteWorkflow.name}`
)
) {
toaster({
title: t('toast.problemDeletingWorkflow'),
status: 'error',
});
}
onError && onError();
}
},
[_deleteWorkflow, toaster, t, onSuccess, onError]
[_deleteWorkflow, toaster, t, onSuccess, onError, toast]
);
return { deleteWorkflow, deleteWorkflowResult };

View File

@ -1,9 +1,13 @@
import { useToast } from '@invoke-ai/ui';
import { useAppToaster } from 'app/components/Toaster';
import { useAppDispatch } from 'app/store/storeHooks';
import { workflowLoadRequested } from 'features/nodes/store/actions';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyGetWorkflowQuery } from 'services/api/endpoints/workflows';
import {
useLazyGetWorkflowQuery,
workflowsApi,
} from 'services/api/endpoints/workflows';
type UseGetAndLoadLibraryWorkflowOptions = {
onSuccess?: () => void;
@ -25,6 +29,7 @@ export const useGetAndLoadLibraryWorkflow: UseGetAndLoadLibraryWorkflow = ({
}) => {
const dispatch = useAppDispatch();
const toaster = useAppToaster();
const toast = useToast();
const { t } = useTranslation();
const [_getAndLoadWorkflow, getAndLoadWorkflowResult] =
useLazyGetWorkflowQuery();
@ -38,14 +43,20 @@ export const useGetAndLoadLibraryWorkflow: UseGetAndLoadLibraryWorkflow = ({
// No toast - the listener for this action does that after the workflow is loaded
onSuccess && onSuccess();
} catch {
toaster({
title: t('toast.problemRetrievingWorkflow'),
status: 'error',
});
if (
!toast.isActive(
`auth-error-toast-${workflowsApi.endpoints.getWorkflow.name}`
)
) {
toaster({
title: t('toast.problemRetrievingWorkflow'),
status: 'error',
});
}
onError && onError();
}
},
[_getAndLoadWorkflow, dispatch, onSuccess, toaster, t, onError]
[_getAndLoadWorkflow, dispatch, onSuccess, toaster, t, onError, toast]
);
return { getAndLoadWorkflow, getAndLoadWorkflowResult };

View File

@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next';
import {
useCreateWorkflowMutation,
useUpdateWorkflowMutation,
workflowsApi,
} from 'services/api/endpoints/workflows';
import type { O } from 'ts-toolbelt';
@ -60,12 +61,23 @@ export const useSaveLibraryWorkflow: UseSaveLibraryWorkflow = () => {
isClosable: true,
});
} catch (e) {
toast.update(toastRef.current, {
title: t('workflows.problemSavingWorkflow'),
status: 'error',
duration: 1000,
isClosable: true,
});
if (
!toast.isActive(
`auth-error-toast-${workflowsApi.endpoints.createWorkflow.name}`
) &&
!toast.isActive(
`auth-error-toast-${workflowsApi.endpoints.updateWorkflow.name}`
)
) {
toast.update(toastRef.current, {
title: t('workflows.problemSavingWorkflow'),
status: 'error',
duration: 1000,
isClosable: true,
});
} else {
toast.close(toastRef.current);
}
}
}, [updateWorkflow, dispatch, toast, t, createWorkflow]);
return {

View File

@ -9,7 +9,10 @@ import {
} from 'features/nodes/store/workflowSlice';
import { useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useCreateWorkflowMutation } from 'services/api/endpoints/workflows';
import {
useCreateWorkflowMutation,
workflowsApi,
} from 'services/api/endpoints/workflows';
type SaveWorkflowAsArg = {
name: string;
@ -59,12 +62,20 @@ export const useSaveWorkflowAs: UseSaveWorkflowAs = () => {
});
} catch (e) {
onError && onError();
toast.update(toastRef.current, {
title: t('workflows.problemSavingWorkflow'),
status: 'error',
duration: 1000,
isClosable: true,
});
if (
!toast.isActive(
`auth-error-toast-${workflowsApi.endpoints.createWorkflow.name}`
)
) {
toast.update(toastRef.current, {
title: t('workflows.problemSavingWorkflow'),
status: 'error',
duration: 1000,
isClosable: true,
});
} else {
toast.close(toastRef.current);
}
}
},
[toast, createWorkflow, dispatch, t]

View File

@ -27,7 +27,8 @@ export const authToastMiddleware: Middleware =
if (isRejectedWithValue(action)) {
try {
const parsed = zRejectedForbiddenAction.parse(action);
if (parsed.meta?.arg?.endpointName === 'getImageDTO') {
const endpointName = parsed.meta?.arg?.endpointName;
if (endpointName === 'getImageDTO') {
// do not show toast if problem is image access
return;
}
@ -39,6 +40,7 @@ export const authToastMiddleware: Middleware =
: undefined;
dispatch(
addToast({
id: `auth-error-toast-${endpointName}`,
title: t('common.somethingWentWrong'),
status: 'error',
description: customMessage,