mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): add retry buttons to queue tab
- Add the new HTTP endpoint to the queue client - Add buttons to the queue items to retry them
This commit is contained in:
@@ -4,11 +4,12 @@ import QueueStatusBadge from 'features/queue/components/common/QueueStatusBadge'
|
||||
import { useDestinationText } from 'features/queue/components/QueueList/useDestinationText';
|
||||
import { useOriginText } from 'features/queue/components/QueueList/useOriginText';
|
||||
import { useCancelQueueItem } from 'features/queue/hooks/useCancelQueueItem';
|
||||
import { useRetryQueueItem } from 'features/queue/hooks/useRetryQueueItem';
|
||||
import { getSecondsFromTimestamps } from 'features/queue/util/getSecondsFromTimestamps';
|
||||
import type { MouseEvent } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
import { PiArrowCounterClockwiseBold, PiXBold } from 'react-icons/pi';
|
||||
import type { SessionQueueItemDTO } from 'services/api/types';
|
||||
|
||||
import { COLUMN_WIDTHS } from './constants';
|
||||
@@ -33,7 +34,7 @@ const QueueItemComponent = ({ index, item, context }: InnerItemProps) => {
|
||||
const handleToggle = useCallback(() => {
|
||||
context.toggleQueueItem(item.item_id);
|
||||
}, [context, item.item_id]);
|
||||
const { cancelQueueItem, isLoading } = useCancelQueueItem(item.item_id);
|
||||
const { cancelQueueItem, isLoading: isLoadingCancelQueueItem } = useCancelQueueItem(item.item_id);
|
||||
const handleCancelQueueItem = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
@@ -41,6 +42,14 @@ const QueueItemComponent = ({ index, item, context }: InnerItemProps) => {
|
||||
},
|
||||
[cancelQueueItem]
|
||||
);
|
||||
const { retryQueueItem, isLoading: isLoadingRetryQueueItem } = useRetryQueueItem(item.item_id);
|
||||
const handleRetryQueueItem = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
retryQueueItem();
|
||||
},
|
||||
[retryQueueItem]
|
||||
);
|
||||
const isOpen = useMemo(() => context.openQueueItems.includes(item.item_id), [context.openQueueItems, item.item_id]);
|
||||
|
||||
const executionTime = useMemo(() => {
|
||||
@@ -52,10 +61,10 @@ const QueueItemComponent = ({ index, item, context }: InnerItemProps) => {
|
||||
}, [item]);
|
||||
|
||||
const isCanceled = useMemo(() => ['canceled', 'completed', 'failed'].includes(item.status), [item.status]);
|
||||
const isFailed = useMemo(() => ['canceled', 'failed'].includes(item.status), [item.status]);
|
||||
const originText = useOriginText(item.origin);
|
||||
const destinationText = useDestinationText(item.destination);
|
||||
|
||||
const icon = useMemo(() => <PiXBold />, []);
|
||||
return (
|
||||
<Flex
|
||||
flexDir="column"
|
||||
@@ -109,13 +118,23 @@ const QueueItemComponent = ({ index, item, context }: InnerItemProps) => {
|
||||
</Flex>
|
||||
<Flex alignItems="center" w={COLUMN_WIDTHS.actions} pe={3}>
|
||||
<ButtonGroup size="xs" variant="ghost">
|
||||
<IconButton
|
||||
onClick={handleCancelQueueItem}
|
||||
isDisabled={isCanceled}
|
||||
isLoading={isLoading}
|
||||
aria-label={t('queue.cancelItem')}
|
||||
icon={icon}
|
||||
/>
|
||||
{!isFailed && (
|
||||
<IconButton
|
||||
onClick={handleCancelQueueItem}
|
||||
isDisabled={isCanceled}
|
||||
isLoading={isLoadingCancelQueueItem}
|
||||
aria-label={t('queue.cancelItem')}
|
||||
icon={<PiXBold />}
|
||||
/>
|
||||
)}
|
||||
{isFailed && (
|
||||
<IconButton
|
||||
onClick={handleRetryQueueItem}
|
||||
isLoading={isLoadingRetryQueueItem}
|
||||
aria-label={t('queue.retryItem')}
|
||||
icon={<PiArrowCounterClockwiseBold />}
|
||||
/>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -4,12 +4,13 @@ import { useDestinationText } from 'features/queue/components/QueueList/useDesti
|
||||
import { useOriginText } from 'features/queue/components/QueueList/useOriginText';
|
||||
import { useCancelBatch } from 'features/queue/hooks/useCancelBatch';
|
||||
import { useCancelQueueItem } from 'features/queue/hooks/useCancelQueueItem';
|
||||
import { useRetryQueueItem } from 'features/queue/hooks/useRetryQueueItem';
|
||||
import { getSecondsFromTimestamps } from 'features/queue/util/getSecondsFromTimestamps';
|
||||
import { get } from 'lodash-es';
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
import { PiArrowCounterClockwiseBold, PiXBold } from 'react-icons/pi';
|
||||
import { useGetQueueItemQuery } from 'services/api/endpoints/queue';
|
||||
import type { SessionQueueItemDTO } from 'services/api/types';
|
||||
|
||||
@@ -20,9 +21,10 @@ type Props = {
|
||||
const QueueItemComponent = ({ queueItemDTO }: Props) => {
|
||||
const { session_id, batch_id, item_id, origin, destination } = queueItemDTO;
|
||||
const { t } = useTranslation();
|
||||
const { cancelBatch, isLoading: isLoadingCancelBatch, isCanceled } = useCancelBatch(batch_id);
|
||||
const { cancelBatch, isLoading: isLoadingCancelBatch, isCanceled: isBatchCanceled } = useCancelBatch(batch_id);
|
||||
|
||||
const { cancelQueueItem, isLoading: isLoadingCancelQueueItem } = useCancelQueueItem(item_id);
|
||||
const { retryQueueItem, isLoading: isLoadingRetryQueueItem } = useRetryQueueItem(item_id);
|
||||
|
||||
const { data: queueItem } = useGetQueueItemQuery(item_id);
|
||||
|
||||
@@ -43,6 +45,13 @@ const QueueItemComponent = ({ queueItemDTO }: Props) => {
|
||||
return `${seconds}s`;
|
||||
}, [queueItem, t]);
|
||||
|
||||
const isCanceled = useMemo(
|
||||
() => !!queueItem && ['canceled', 'completed', 'failed'].includes(queueItem.status),
|
||||
[queueItem]
|
||||
);
|
||||
|
||||
const isFailed = useMemo(() => !!queueItem && ['canceled', 'failed'].includes(queueItem.status), [queueItem]);
|
||||
|
||||
return (
|
||||
<Flex layerStyle="third" flexDir="column" p={2} pt={0} borderRadius="base" gap={2}>
|
||||
<Flex
|
||||
@@ -61,20 +70,34 @@ const QueueItemComponent = ({ queueItemDTO }: Props) => {
|
||||
<QueueItemData label={t('queue.batch')} data={batch_id} />
|
||||
<QueueItemData label={t('queue.session')} data={session_id} />
|
||||
<ButtonGroup size="xs" orientation="vertical">
|
||||
<Button
|
||||
onClick={cancelQueueItem}
|
||||
isLoading={isLoadingCancelQueueItem}
|
||||
isDisabled={queueItem ? ['canceled', 'completed', 'failed'].includes(queueItem.status) : true}
|
||||
aria-label={t('queue.cancelItem')}
|
||||
leftIcon={<PiXBold />}
|
||||
colorScheme="error"
|
||||
>
|
||||
{t('queue.cancelItem')}
|
||||
</Button>
|
||||
{!isFailed && (
|
||||
<Button
|
||||
onClick={cancelQueueItem}
|
||||
isLoading={isLoadingCancelQueueItem}
|
||||
isDisabled={queueItem ? isCanceled : true}
|
||||
aria-label={t('queue.cancelItem')}
|
||||
leftIcon={<PiXBold />}
|
||||
colorScheme="error"
|
||||
>
|
||||
{t('queue.cancelItem')}
|
||||
</Button>
|
||||
)}
|
||||
{isFailed && (
|
||||
<Button
|
||||
onClick={retryQueueItem}
|
||||
isLoading={isLoadingRetryQueueItem}
|
||||
isDisabled={!queueItem}
|
||||
aria-label={t('queue.retryItem')}
|
||||
leftIcon={<PiArrowCounterClockwiseBold />}
|
||||
colorScheme="invokeBlue"
|
||||
>
|
||||
{t('queue.retryItem')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={cancelBatch}
|
||||
isLoading={isLoadingCancelBatch}
|
||||
isDisabled={isCanceled}
|
||||
isDisabled={isBatchCanceled}
|
||||
aria-label={t('queue.cancelBatch')}
|
||||
leftIcon={<PiXBold />}
|
||||
colorScheme="error"
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRetryItemsByIdMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useRetryQueueItem = (item_id: number) => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useRetryItemsByIdMutation();
|
||||
const { t } = useTranslation();
|
||||
const retryQueueItem = useCallback(async () => {
|
||||
try {
|
||||
const result = await trigger([item_id]).unwrap();
|
||||
if (!result.retried_item_ids.includes(item_id)) {
|
||||
throw new Error('Failed to retry item');
|
||||
}
|
||||
toast({
|
||||
id: 'QUEUE_RETRY_SUCCEEDED',
|
||||
title: t('queue.retrySucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_RETRY_FAILED',
|
||||
title: t('queue.retryFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [item_id, t, trigger]);
|
||||
|
||||
return { retryQueueItem, isLoading, isDisabled: !isConnected };
|
||||
};
|
||||
Reference in New Issue
Block a user