mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-14 23:45:07 -05:00
refactor(ui): migrate from canceling queue items to deleteing, make queue hook APIs consistent
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useCancelAllExceptCurrentQueueItemDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXCircle } from 'react-icons/pi';
|
||||
|
||||
export const CancelAllExceptCurrentIconButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const cancelAllExceptCurrent = useCancelAllExceptCurrentQueueItemDialog();
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
size="lg"
|
||||
isDisabled={cancelAllExceptCurrent.isDisabled}
|
||||
isLoading={cancelAllExceptCurrent.isLoading}
|
||||
aria-label={t('queue.clear')}
|
||||
tooltip={t('queue.cancelAllExceptCurrentTooltip')}
|
||||
icon={<PiXCircle />}
|
||||
colorScheme="error"
|
||||
onClick={cancelAllExceptCurrent.openDialog}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
CancelAllExceptCurrentIconButton.displayName = 'CancelAllExceptCurrentIconButton';
|
||||
@@ -9,16 +9,15 @@ const [useCancelAllExceptCurrentQueueItemConfirmationAlertDialog] = buildUseBool
|
||||
|
||||
export const useCancelAllExceptCurrentQueueItemDialog = () => {
|
||||
const dialog = useCancelAllExceptCurrentQueueItemConfirmationAlertDialog();
|
||||
const { cancelAllExceptCurrentQueueItem, isLoading, isDisabled, queueStatus } = useCancelAllExceptCurrentQueueItem();
|
||||
const cancelAllExceptCurrentQueueItem = useCancelAllExceptCurrentQueueItem();
|
||||
|
||||
return {
|
||||
cancelAllExceptCurrentQueueItem,
|
||||
trigger: cancelAllExceptCurrentQueueItem.trigger,
|
||||
isOpen: dialog.isTrue,
|
||||
openDialog: dialog.setTrue,
|
||||
closeDialog: dialog.setFalse,
|
||||
isLoading,
|
||||
queueStatus,
|
||||
isDisabled,
|
||||
isLoading: cancelAllExceptCurrentQueueItem.isLoading,
|
||||
isDisabled: cancelAllExceptCurrentQueueItem.isDisabled,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,7 +31,7 @@ export const CancelAllExceptCurrentQueueItemConfirmationAlertDialog = memo(() =>
|
||||
isOpen={cancelAllExceptCurrentQueueItem.isOpen}
|
||||
onClose={cancelAllExceptCurrentQueueItem.closeDialog}
|
||||
title={t('queue.cancelAllExceptCurrentTooltip')}
|
||||
acceptCallback={cancelAllExceptCurrentQueueItem.cancelAllExceptCurrentQueueItem}
|
||||
acceptCallback={cancelAllExceptCurrentQueueItem.trigger}
|
||||
acceptButtonText={t('queue.confirm')}
|
||||
useInert={false}
|
||||
>
|
||||
|
||||
@@ -5,10 +5,14 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
const ClearInvocationCacheButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const { clearInvocationCache, isDisabled, isLoading } = useClearInvocationCache();
|
||||
const clearInvocationCache = useClearInvocationCache();
|
||||
|
||||
return (
|
||||
<Button isDisabled={isDisabled} isLoading={isLoading} onClick={clearInvocationCache}>
|
||||
<Button
|
||||
onClick={clearInvocationCache.trigger}
|
||||
isDisabled={clearInvocationCache.isDisabled}
|
||||
isLoading={clearInvocationCache.isLoading}
|
||||
>
|
||||
{t('invocationCache.clear')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -9,16 +9,15 @@ const [useClearQueueConfirmationAlertDialog] = buildUseBoolean(false);
|
||||
|
||||
const useClearQueueDialog = () => {
|
||||
const dialog = useClearQueueConfirmationAlertDialog();
|
||||
const { clearQueue, isLoading, isDisabled, queueStatus } = useClearQueue();
|
||||
const clearQueue = useClearQueue();
|
||||
|
||||
return {
|
||||
clearQueue,
|
||||
isOpen: dialog.isTrue,
|
||||
openDialog: dialog.setTrue,
|
||||
closeDialog: dialog.setFalse,
|
||||
isLoading,
|
||||
queueStatus,
|
||||
isDisabled,
|
||||
trigger: clearQueue.trigger,
|
||||
isLoading: clearQueue.isLoading,
|
||||
isDisabled: clearQueue.isDisabled,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,7 +31,7 @@ export const ClearQueueConfirmationsAlertDialog = memo(() => {
|
||||
isOpen={clearQueue.isOpen}
|
||||
onClose={clearQueue.closeDialog}
|
||||
title={t('queue.clearTooltip')}
|
||||
acceptCallback={clearQueue.clearQueue}
|
||||
acceptCallback={clearQueue.trigger}
|
||||
acceptButtonText={t('queue.clear')}
|
||||
useInert={false}
|
||||
>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { IconButton, useShiftModifier } from '@invoke-ai/ui-library';
|
||||
import { useCancelAllExceptCurrentQueueItemDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold, PiXCircle } from 'react-icons/pi';
|
||||
|
||||
export const ClearQueueIconButton = memo(() => {
|
||||
const shift = useShiftModifier();
|
||||
|
||||
if (!shift) {
|
||||
return <CancelCurrentIconButton />;
|
||||
}
|
||||
|
||||
return <CancelAllExceptCurrentIconButton />;
|
||||
});
|
||||
|
||||
ClearQueueIconButton.displayName = 'ClearQueueIconButton';
|
||||
|
||||
const CancelCurrentIconButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const cancelCurrentQueueItem = useCancelCurrentQueueItem();
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
size="lg"
|
||||
isDisabled={cancelCurrentQueueItem.isDisabled}
|
||||
isLoading={cancelCurrentQueueItem.isLoading}
|
||||
aria-label={t('queue.cancel')}
|
||||
tooltip={t('queue.cancelTooltip')}
|
||||
icon={<PiXBold />}
|
||||
colorScheme="error"
|
||||
onClick={cancelCurrentQueueItem.cancelQueueItem}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
CancelCurrentIconButton.displayName = 'CancelCurrentIconButton';
|
||||
|
||||
const CancelAllExceptCurrentIconButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const cancelAllExceptCurrent = useCancelAllExceptCurrentQueueItemDialog();
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
size="lg"
|
||||
isDisabled={cancelAllExceptCurrent.isDisabled}
|
||||
isLoading={cancelAllExceptCurrent.isLoading}
|
||||
aria-label={t('queue.clear')}
|
||||
tooltip={t('queue.cancelAllExceptCurrentTooltip')}
|
||||
icon={<PiXCircle />}
|
||||
colorScheme="error"
|
||||
onClick={cancelAllExceptCurrent.openDialog}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
CancelAllExceptCurrentIconButton.displayName = 'CancelAllExceptCurrentIconButton';
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ConfirmationAlertDialog, Text } from '@invoke-ai/ui-library';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { buildUseBoolean } from 'common/hooks/useBoolean';
|
||||
import { useDeleteAllExceptCurrentQueueItem } from 'features/queue/hooks/useDeleteAllExceptCurrentQueueItem';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const [useDeleteAllExceptCurrentQueueItemConfirmationAlertDialog] = buildUseBoolean(false);
|
||||
|
||||
export const useDeleteAllExceptCurrentQueueItemDialog = () => {
|
||||
const dialog = useDeleteAllExceptCurrentQueueItemConfirmationAlertDialog();
|
||||
const deleteAllExceptCurrentQueueItem = useDeleteAllExceptCurrentQueueItem();
|
||||
|
||||
return {
|
||||
trigger: deleteAllExceptCurrentQueueItem.trigger,
|
||||
isOpen: dialog.isTrue,
|
||||
openDialog: dialog.setTrue,
|
||||
closeDialog: dialog.setFalse,
|
||||
isLoading: deleteAllExceptCurrentQueueItem.isLoading,
|
||||
isDisabled: deleteAllExceptCurrentQueueItem.isDisabled,
|
||||
};
|
||||
};
|
||||
|
||||
export const DeleteAllExceptCurrentQueueItemConfirmationAlertDialog = memo(() => {
|
||||
useAssertSingleton('DeleteAllExceptCurrentQueueItemConfirmationAlertDialog');
|
||||
const { t } = useTranslation();
|
||||
const deleteAllExceptCurrentQueueItem = useDeleteAllExceptCurrentQueueItemDialog();
|
||||
|
||||
return (
|
||||
<ConfirmationAlertDialog
|
||||
isOpen={deleteAllExceptCurrentQueueItem.isOpen}
|
||||
onClose={deleteAllExceptCurrentQueueItem.closeDialog}
|
||||
title={t('queue.cancelAllExceptCurrentTooltip')}
|
||||
acceptCallback={deleteAllExceptCurrentQueueItem.trigger}
|
||||
acceptButtonText={t('queue.confirm')}
|
||||
useInert={false}
|
||||
>
|
||||
<Text>{t('queue.cancelAllExceptCurrentQueueItemAlertDialog')}</Text>
|
||||
<br />
|
||||
<Text>{t('queue.cancelAllExceptCurrentQueueItemAlertDialog2')}</Text>
|
||||
</ConfirmationAlertDialog>
|
||||
);
|
||||
});
|
||||
|
||||
DeleteAllExceptCurrentQueueItemConfirmationAlertDialog.displayName =
|
||||
'DeleteAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
@@ -0,0 +1,25 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useDeleteCurrentQueueItem } from 'features/queue/hooks/useDeleteCurrentQueueItem';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
|
||||
export const DeleteCurrentQueueItemIconButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const deleteCurrentQueueItem = useDeleteCurrentQueueItem();
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
size="lg"
|
||||
onClick={deleteCurrentQueueItem.trigger}
|
||||
isDisabled={deleteCurrentQueueItem.isDisabled}
|
||||
isLoading={deleteCurrentQueueItem.isLoading}
|
||||
aria-label={t('queue.cancel')}
|
||||
tooltip={t('queue.cancelTooltip')}
|
||||
icon={<PiXBold />}
|
||||
colorScheme="error"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
DeleteCurrentQueueItemIconButton.displayName = 'DeleteCurrentQueueItemIconButton';
|
||||
@@ -11,17 +11,17 @@ type Props = {
|
||||
|
||||
const PauseProcessorButton = ({ asIconButton }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { pauseProcessor, isLoading, isDisabled } = usePauseProcessor();
|
||||
const pauseProcessor = usePauseProcessor();
|
||||
|
||||
return (
|
||||
<QueueButton
|
||||
asIconButton={asIconButton}
|
||||
label={t('queue.pause')}
|
||||
tooltip={t('queue.pauseTooltip')}
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
isDisabled={pauseProcessor.isDisabled}
|
||||
isLoading={pauseProcessor.isLoading}
|
||||
icon={<PiPauseFill />}
|
||||
onClick={pauseProcessor}
|
||||
onClick={pauseProcessor.trigger}
|
||||
colorScheme="gold"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { usePruneQueue } from 'features/queue/hooks/usePruneQueue';
|
||||
import { useFinishedCount, usePruneQueue } from 'features/queue/hooks/usePruneQueue';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiBroomBold } from 'react-icons/pi';
|
||||
@@ -11,17 +11,18 @@ type Props = {
|
||||
|
||||
const PruneQueueButton = ({ asIconButton }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { pruneQueue, isLoading, finishedCount, isDisabled } = usePruneQueue();
|
||||
const pruneQueue = usePruneQueue();
|
||||
const finishedCount = useFinishedCount();
|
||||
|
||||
return (
|
||||
<QueueButton
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
onClick={pruneQueue.trigger}
|
||||
isDisabled={pruneQueue.isDisabled}
|
||||
isLoading={pruneQueue.isLoading}
|
||||
asIconButton={asIconButton}
|
||||
label={t('queue.prune')}
|
||||
tooltip={t('queue.pruneTooltip', { item_count: finishedCount })}
|
||||
icon={<PiBroomBold />}
|
||||
onClick={pruneQueue}
|
||||
colorScheme="invokeBlue"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { IconButton, Menu, MenuButton, MenuGroup, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { SessionMenuItems } from 'common/components/SessionMenuItems';
|
||||
import { useCancelAllExceptCurrentQueueItemDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { useDeleteAllExceptCurrentQueueItemDialog } from 'features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { QueueCountBadge } from 'features/queue/components/QueueCountBadge';
|
||||
import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem';
|
||||
import { useDeleteCurrentQueueItem } from 'features/queue/hooks/useDeleteCurrentQueueItem';
|
||||
import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor';
|
||||
import { useResumeProcessor } from 'features/queue/hooks/useResumeProcessor';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
@@ -18,18 +18,10 @@ export const QueueActionsMenuButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const isPauseEnabled = useFeatureStatus('pauseQueue');
|
||||
const isResumeEnabled = useFeatureStatus('resumeQueue');
|
||||
const cancelAllExceptCurrent = useCancelAllExceptCurrentQueueItemDialog();
|
||||
const cancelCurrent = useCancelCurrentQueueItem();
|
||||
const {
|
||||
resumeProcessor,
|
||||
isLoading: isLoadingResumeProcessor,
|
||||
isDisabled: isDisabledResumeProcessor,
|
||||
} = useResumeProcessor();
|
||||
const {
|
||||
pauseProcessor,
|
||||
isLoading: isLoadingPauseProcessor,
|
||||
isDisabled: isDisabledPauseProcessor,
|
||||
} = usePauseProcessor();
|
||||
const deleteAllExceptCurrent = useDeleteAllExceptCurrentQueueItemDialog();
|
||||
const deleteCurrentQueueItem = useDeleteCurrentQueueItem();
|
||||
const resumeProcessor = useResumeProcessor();
|
||||
const pauseProcessor = usePauseProcessor();
|
||||
const openQueue = useCallback(() => {
|
||||
dispatch(setActiveTab('queue'));
|
||||
}, [dispatch]);
|
||||
@@ -46,27 +38,27 @@ export const QueueActionsMenuButton = memo(() => {
|
||||
<MenuItem
|
||||
isDestructive
|
||||
icon={<PiXBold />}
|
||||
onClick={cancelCurrent.cancelQueueItem}
|
||||
isLoading={cancelCurrent.isLoading}
|
||||
isDisabled={cancelCurrent.isDisabled}
|
||||
onClick={deleteCurrentQueueItem.trigger}
|
||||
isLoading={deleteCurrentQueueItem.isLoading}
|
||||
isDisabled={deleteCurrentQueueItem.isDisabled}
|
||||
>
|
||||
{t('queue.cancelTooltip')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
isDestructive
|
||||
icon={<PiXCircle />}
|
||||
onClick={cancelAllExceptCurrent.openDialog}
|
||||
isLoading={cancelAllExceptCurrent.isLoading}
|
||||
isDisabled={cancelAllExceptCurrent.isDisabled}
|
||||
onClick={deleteAllExceptCurrent.openDialog}
|
||||
isLoading={deleteAllExceptCurrent.isLoading}
|
||||
isDisabled={deleteAllExceptCurrent.isDisabled}
|
||||
>
|
||||
{t('queue.cancelAllExceptCurrentTooltip')}
|
||||
</MenuItem>
|
||||
{isResumeEnabled && (
|
||||
<MenuItem
|
||||
icon={<PiPlayFill />}
|
||||
onClick={resumeProcessor}
|
||||
isLoading={isLoadingResumeProcessor}
|
||||
isDisabled={isDisabledResumeProcessor}
|
||||
onClick={resumeProcessor.trigger}
|
||||
isLoading={resumeProcessor.isLoading}
|
||||
isDisabled={resumeProcessor.isDisabled}
|
||||
>
|
||||
{t('queue.resumeTooltip')}
|
||||
</MenuItem>
|
||||
@@ -74,9 +66,9 @@ export const QueueActionsMenuButton = memo(() => {
|
||||
{isPauseEnabled && (
|
||||
<MenuItem
|
||||
icon={<PiPauseFill />}
|
||||
onClick={pauseProcessor}
|
||||
isLoading={isLoadingPauseProcessor}
|
||||
isDisabled={isDisabledPauseProcessor}
|
||||
onClick={pauseProcessor.trigger}
|
||||
isLoading={pauseProcessor.isLoading}
|
||||
isDisabled={pauseProcessor.isDisabled}
|
||||
>
|
||||
{t('queue.pauseTooltip')}
|
||||
</MenuItem>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Flex, Spacer } from '@invoke-ai/ui-library';
|
||||
import { ClearQueueIconButton } from 'features/queue/components/ClearQueueIconButton';
|
||||
import { Flex, Spacer, useShiftModifier } from '@invoke-ai/ui-library';
|
||||
import { DeleteAllExceptCurrentQueueItemConfirmationAlertDialog } from 'features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { DeleteCurrentQueueItemIconButton } from 'features/queue/components/DeleteCurrentQueueItemIconButton';
|
||||
import { QueueActionsMenuButton } from 'features/queue/components/QueueActionsMenuButton';
|
||||
import ProgressBar from 'features/system/components/ProgressBar';
|
||||
import { memo } from 'react';
|
||||
@@ -13,7 +14,7 @@ const QueueControls = () => {
|
||||
<InvokeButton />
|
||||
<Spacer />
|
||||
<QueueActionsMenuButton />
|
||||
<ClearQueueIconButton />
|
||||
<DeleteIconButton />
|
||||
</Flex>
|
||||
<ProgressBar />
|
||||
</Flex>
|
||||
@@ -21,3 +22,15 @@ const QueueControls = () => {
|
||||
};
|
||||
|
||||
export default memo(QueueControls);
|
||||
|
||||
export const DeleteIconButton = memo(() => {
|
||||
const shift = useShiftModifier();
|
||||
|
||||
if (!shift) {
|
||||
return <DeleteCurrentQueueItemIconButton />;
|
||||
}
|
||||
|
||||
return <DeleteAllExceptCurrentQueueItemConfirmationAlertDialog />;
|
||||
});
|
||||
|
||||
DeleteIconButton.displayName = 'DeleteIconButton';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Badge, ButtonGroup, Collapse, Flex, IconButton, Text } from '@invoke-ai
|
||||
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 { useDeleteQueueItem } from 'features/queue/hooks/useDeleteQueueItem';
|
||||
import { useRetryQueueItem } from 'features/queue/hooks/useRetryQueueItem';
|
||||
import { getSecondsFromTimestamps } from 'features/queue/util/getSecondsFromTimestamps';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
@@ -38,21 +38,21 @@ const QueueItemComponent = ({ index, item, context }: InnerItemProps) => {
|
||||
const handleToggle = useCallback(() => {
|
||||
context.toggleQueueItem(item.item_id);
|
||||
}, [context, item.item_id]);
|
||||
const { cancelQueueItem, isLoading: isLoadingCancelQueueItem } = useCancelQueueItem(item.item_id);
|
||||
const handleCancelQueueItem = useCallback(
|
||||
const deleteQueueItem = useDeleteQueueItem();
|
||||
const onClickDeleteQueueItem = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
cancelQueueItem();
|
||||
deleteQueueItem.trigger(item.item_id);
|
||||
},
|
||||
[cancelQueueItem]
|
||||
[deleteQueueItem, item.item_id]
|
||||
);
|
||||
const { retryQueueItem, isLoading: isLoadingRetryQueueItem } = useRetryQueueItem(item.item_id);
|
||||
const handleRetryQueueItem = useCallback(
|
||||
const retryQueueItem = useRetryQueueItem();
|
||||
const onClickRetryQueueItem = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
retryQueueItem();
|
||||
retryQueueItem.trigger(item.item_id);
|
||||
},
|
||||
[retryQueueItem]
|
||||
[item.item_id, retryQueueItem]
|
||||
);
|
||||
const isOpen = useMemo(() => context.openQueueItems.includes(item.item_id), [context.openQueueItems, item.item_id]);
|
||||
|
||||
@@ -135,17 +135,17 @@ const QueueItemComponent = ({ index, item, context }: InnerItemProps) => {
|
||||
<ButtonGroup size="xs" variant="ghost">
|
||||
{(!isFailed || !isRetryEnabled || isValidationRun) && (
|
||||
<IconButton
|
||||
onClick={handleCancelQueueItem}
|
||||
onClick={onClickDeleteQueueItem}
|
||||
isDisabled={isCanceled}
|
||||
isLoading={isLoadingCancelQueueItem}
|
||||
isLoading={deleteQueueItem.isLoading}
|
||||
aria-label={t('queue.cancelItem')}
|
||||
icon={<PiXBold />}
|
||||
/>
|
||||
)}
|
||||
{isFailed && isRetryEnabled && !isValidationRun && (
|
||||
<IconButton
|
||||
onClick={handleRetryQueueItem}
|
||||
isLoading={isLoadingRetryQueueItem}
|
||||
onClick={onClickRetryQueueItem}
|
||||
isLoading={retryQueueItem.isLoading}
|
||||
aria-label={t('queue.retryItem')}
|
||||
icon={<PiArrowCounterClockwiseBold />}
|
||||
/>
|
||||
|
||||
@@ -2,14 +2,15 @@ import { Button, ButtonGroup, Flex, Heading, Spinner, Text } from '@invoke-ai/ui
|
||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||
import { useDestinationText } from 'features/queue/components/QueueList/useDestinationText';
|
||||
import { useOriginText } from 'features/queue/components/QueueList/useOriginText';
|
||||
import { useBatchIsCanceled } from 'features/queue/hooks/useBatchIsCanceled';
|
||||
import { useCancelBatch } from 'features/queue/hooks/useCancelBatch';
|
||||
import { useCancelQueueItem } from 'features/queue/hooks/useCancelQueueItem';
|
||||
import { useDeleteQueueItem } from 'features/queue/hooks/useDeleteQueueItem';
|
||||
import { useRetryQueueItem } from 'features/queue/hooks/useRetryQueueItem';
|
||||
import { getSecondsFromTimestamps } from 'features/queue/util/getSecondsFromTimestamps';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { get } from 'lodash-es';
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiArrowCounterClockwiseBold, PiXBold } from 'react-icons/pi';
|
||||
import type { S } from 'services/api/types';
|
||||
@@ -22,9 +23,10 @@ const QueueItemComponent = ({ queueItem }: Props) => {
|
||||
const { session_id, batch_id, item_id, origin, destination } = queueItem;
|
||||
const { t } = useTranslation();
|
||||
const isRetryEnabled = useFeatureStatus('retryQueueItem');
|
||||
const { cancelBatch, isLoading: isLoadingCancelBatch, isCanceled: isBatchCanceled } = useCancelBatch(batch_id);
|
||||
const { cancelQueueItem, isLoading: isLoadingCancelQueueItem } = useCancelQueueItem(item_id);
|
||||
const { retryQueueItem, isLoading: isLoadingRetryQueueItem } = useRetryQueueItem(item_id);
|
||||
const isBatchCanceled = useBatchIsCanceled(batch_id);
|
||||
const cancelBatch = useCancelBatch();
|
||||
const deleteQueueItem = useDeleteQueueItem();
|
||||
const retryQueueItem = useRetryQueueItem();
|
||||
|
||||
const originText = useOriginText(origin);
|
||||
const destinationText = useDestinationText(destination);
|
||||
@@ -50,6 +52,18 @@ const QueueItemComponent = ({ queueItem }: Props) => {
|
||||
|
||||
const isFailed = useMemo(() => !!queueItem && ['canceled', 'failed'].includes(queueItem.status), [queueItem]);
|
||||
|
||||
const onCancelBatch = useCallback(() => {
|
||||
cancelBatch.trigger(batch_id);
|
||||
}, [cancelBatch, batch_id]);
|
||||
|
||||
const onCancelQueueItem = useCallback(() => {
|
||||
deleteQueueItem.trigger(item_id);
|
||||
}, [deleteQueueItem, item_id]);
|
||||
|
||||
const onRetryQueueItem = useCallback(() => {
|
||||
retryQueueItem.trigger(item_id);
|
||||
}, [retryQueueItem, item_id]);
|
||||
|
||||
return (
|
||||
<Flex layerStyle="third" flexDir="column" p={2} pt={0} borderRadius="base" gap={2}>
|
||||
<Flex
|
||||
@@ -70,9 +84,9 @@ const QueueItemComponent = ({ queueItem }: Props) => {
|
||||
<ButtonGroup size="xs" orientation="vertical">
|
||||
{(!isFailed || !isRetryEnabled) && (
|
||||
<Button
|
||||
onClick={cancelQueueItem}
|
||||
isLoading={isLoadingCancelQueueItem}
|
||||
isDisabled={queueItem ? isCanceled : true}
|
||||
onClick={onCancelQueueItem}
|
||||
isLoading={deleteQueueItem.isLoading}
|
||||
isDisabled={deleteQueueItem.isDisabled || queueItem ? isCanceled : true}
|
||||
aria-label={t('queue.cancelItem')}
|
||||
leftIcon={<PiXBold />}
|
||||
colorScheme="error"
|
||||
@@ -82,9 +96,9 @@ const QueueItemComponent = ({ queueItem }: Props) => {
|
||||
)}
|
||||
{isFailed && isRetryEnabled && (
|
||||
<Button
|
||||
onClick={retryQueueItem}
|
||||
isLoading={isLoadingRetryQueueItem}
|
||||
isDisabled={!queueItem}
|
||||
onClick={onRetryQueueItem}
|
||||
isLoading={retryQueueItem.isLoading}
|
||||
isDisabled={retryQueueItem.isDisabled || !queueItem}
|
||||
aria-label={t('queue.retryItem')}
|
||||
leftIcon={<PiArrowCounterClockwiseBold />}
|
||||
colorScheme="invokeBlue"
|
||||
@@ -93,9 +107,9 @@ const QueueItemComponent = ({ queueItem }: Props) => {
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={cancelBatch}
|
||||
isLoading={isLoadingCancelBatch}
|
||||
isDisabled={isBatchCanceled}
|
||||
onClick={onCancelBatch}
|
||||
isLoading={cancelBatch.isLoading}
|
||||
isDisabled={cancelBatch.isDisabled || isBatchCanceled}
|
||||
aria-label={t('queue.cancelBatch')}
|
||||
leftIcon={<PiXBold />}
|
||||
colorScheme="error"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { ButtonGroup, Flex } from '@invoke-ai/ui-library';
|
||||
import { CancelAllExceptCurrentButton } from 'features/queue/components/CancelAllExceptCurrentButton';
|
||||
import { DeleteAllExceptCurrentQueueItemConfirmationAlertDialog } from 'features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { memo } from 'react';
|
||||
|
||||
@@ -24,7 +24,7 @@ const QueueTabQueueControls = () => {
|
||||
)}
|
||||
<ButtonGroup w={28} orientation="vertical" size="sm">
|
||||
<PruneQueueButton />
|
||||
<CancelAllExceptCurrentButton />
|
||||
<DeleteAllExceptCurrentQueueItemConfirmationAlertDialog />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
<ClearModelCacheButton />
|
||||
|
||||
@@ -11,17 +11,17 @@ type Props = {
|
||||
|
||||
const ResumeProcessorButton = ({ asIconButton }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { resumeProcessor, isLoading, isDisabled } = useResumeProcessor();
|
||||
const resumeProcessor = useResumeProcessor();
|
||||
|
||||
return (
|
||||
<QueueButton
|
||||
asIconButton={asIconButton}
|
||||
label={t('queue.resume')}
|
||||
tooltip={t('queue.resumeTooltip')}
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
isDisabled={resumeProcessor.isDisabled}
|
||||
isLoading={resumeProcessor.isLoading}
|
||||
icon={<PiPlayFill />}
|
||||
onClick={resumeProcessor}
|
||||
onClick={resumeProcessor.trigger}
|
||||
colorScheme="green"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -9,28 +9,28 @@ const ToggleInvocationCacheButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
|
||||
const {
|
||||
enableInvocationCache,
|
||||
isDisabled: isEnableDisabled,
|
||||
isLoading: isEnableLoading,
|
||||
} = useEnableInvocationCache();
|
||||
const enableInvocationCache = useEnableInvocationCache();
|
||||
|
||||
const {
|
||||
disableInvocationCache,
|
||||
isDisabled: isDisableDisabled,
|
||||
isLoading: isDisableLoading,
|
||||
} = useDisableInvocationCache();
|
||||
const disableInvocationCache = useDisableInvocationCache();
|
||||
|
||||
if (cacheStatus?.enabled) {
|
||||
return (
|
||||
<Button isDisabled={isDisableDisabled} isLoading={isDisableLoading} onClick={disableInvocationCache}>
|
||||
<Button
|
||||
onClick={disableInvocationCache.trigger}
|
||||
isDisabled={disableInvocationCache.isDisabled}
|
||||
isLoading={disableInvocationCache.isLoading}
|
||||
>
|
||||
{t('invocationCache.disable')}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button isDisabled={isEnableDisabled} isLoading={isEnableLoading} onClick={enableInvocationCache}>
|
||||
<Button
|
||||
onClick={enableInvocationCache.trigger}
|
||||
isDisabled={enableInvocationCache.isDisabled}
|
||||
isLoading={enableInvocationCache.isLoading}
|
||||
>
|
||||
{t('invocationCache.enable')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useGetBatchStatusQuery } from 'services/api/endpoints/queue';
|
||||
|
||||
export const useBatchIsCanceled = (batch_id: string) => {
|
||||
const { isCanceled } = useGetBatchStatusQuery(
|
||||
{ batch_id },
|
||||
{
|
||||
selectFromResult: ({ data }) => {
|
||||
if (!data) {
|
||||
return { isCanceled: true };
|
||||
}
|
||||
|
||||
return {
|
||||
isCanceled: data?.in_progress === 0 && data?.pending === 0,
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return isCanceled;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useCancelAllExceptCurrentMutation, useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -9,17 +9,17 @@ export const useCancelAllExceptCurrentQueueItem = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useCancelAllExceptCurrentMutation({
|
||||
const [_trigger, { isLoading }] = useCancelAllExceptCurrentMutation({
|
||||
fixedCacheKey: 'cancelAllExceptCurrent',
|
||||
});
|
||||
|
||||
const cancelAllExceptCurrentQueueItem = useCallback(async () => {
|
||||
const trigger = useCallback(async () => {
|
||||
if (!queueStatus?.queue.pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
@@ -32,17 +32,7 @@ export const useCancelAllExceptCurrentQueueItem = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [queueStatus?.queue.pending, trigger, t]);
|
||||
}, [queueStatus?.queue.pending, _trigger, t]);
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() => !isConnected || !queueStatus?.queue.pending,
|
||||
[isConnected, queueStatus?.queue.pending]
|
||||
);
|
||||
|
||||
return {
|
||||
cancelAllExceptCurrentQueueItem,
|
||||
isLoading,
|
||||
queueStatus,
|
||||
isDisabled,
|
||||
};
|
||||
return { trigger, isLoading, isDisabled: !isConnected || !queueStatus?.queue.pending } as const;
|
||||
};
|
||||
|
||||
@@ -2,48 +2,34 @@ import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useCancelByBatchIdsMutation, useGetBatchStatusQuery } from 'services/api/endpoints/queue';
|
||||
import { useCancelByBatchIdsMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useCancelBatch = (batch_id: string) => {
|
||||
export const useCancelBatch = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const { isCanceled } = useGetBatchStatusQuery(
|
||||
{ batch_id },
|
||||
{
|
||||
selectFromResult: ({ data }) => {
|
||||
if (!data) {
|
||||
return { isCanceled: true };
|
||||
}
|
||||
|
||||
return {
|
||||
isCanceled: data?.in_progress === 0 && data?.pending === 0,
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
const [trigger, { isLoading }] = useCancelByBatchIdsMutation({
|
||||
const [_trigger, { isLoading }] = useCancelByBatchIdsMutation({
|
||||
fixedCacheKey: 'cancelByBatchIds',
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const cancelBatch = useCallback(async () => {
|
||||
if (isCanceled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await trigger({ batch_ids: [batch_id] }).unwrap();
|
||||
toast({
|
||||
id: 'CANCEL_BATCH_SUCCEEDED',
|
||||
title: t('queue.cancelBatchSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'CANCEL_BATCH_FAILED',
|
||||
title: t('queue.cancelBatchFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [batch_id, isCanceled, t, trigger]);
|
||||
const trigger = useCallback(
|
||||
async (batch_id: string) => {
|
||||
try {
|
||||
await _trigger({ batch_ids: [batch_id] }).unwrap();
|
||||
toast({
|
||||
id: 'CANCEL_BATCH_SUCCEEDED',
|
||||
title: t('queue.cancelBatchSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'CANCEL_BATCH_FAILED',
|
||||
title: t('queue.cancelBatchFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
},
|
||||
[t, _trigger]
|
||||
);
|
||||
|
||||
return { cancelBatch, isLoading, isCanceled, isDisabled: !isConnected };
|
||||
return { trigger, isLoading, isDisabled: !isConnected };
|
||||
};
|
||||
|
||||
@@ -1,43 +1,20 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useCancelQueueItemMutation, useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
import { useCancelQueueItem } from 'features/queue/hooks/useCancelQueueItem';
|
||||
import { useCurrentQueueItemId } from 'features/queue/hooks/useCurrentQueueItemId';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useCancelCurrentQueueItem = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const [trigger, { isLoading }] = useCancelQueueItemMutation();
|
||||
const { t } = useTranslation();
|
||||
const currentQueueItemId = useMemo(() => queueStatus?.queue.item_id, [queueStatus?.queue.item_id]);
|
||||
const cancelQueueItem = useCallback(async () => {
|
||||
if (currentQueueItemId !== null || currentQueueItemId !== undefined) {
|
||||
const currentQueueItemId = useCurrentQueueItemId();
|
||||
const cancelQueueItem = useCancelQueueItem();
|
||||
const trigger = useCallback(() => {
|
||||
if (currentQueueItemId === null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await trigger({ item_id: currentQueueItemId }).unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_FAILED',
|
||||
title: t('queue.cancelFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [currentQueueItemId, t, trigger]);
|
||||
|
||||
const isDisabled = useMemo(() => !isConnected || isNil(currentQueueItemId), [isConnected, currentQueueItemId]);
|
||||
cancelQueueItem.trigger(currentQueueItemId);
|
||||
}, [currentQueueItemId, cancelQueueItem]);
|
||||
|
||||
return {
|
||||
cancelQueueItem,
|
||||
isLoading,
|
||||
currentQueueItemId,
|
||||
isDisabled,
|
||||
trigger,
|
||||
isLoading: cancelQueueItem.isLoading,
|
||||
isDisabled: cancelQueueItem.isDisabled || currentQueueItemId === null,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,26 +5,29 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useCancelQueueItemMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useCancelQueueItem = (item_id: number) => {
|
||||
export const useCancelQueueItem = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useCancelQueueItemMutation();
|
||||
const [_trigger, { isLoading }] = useCancelQueueItemMutation();
|
||||
const { t } = useTranslation();
|
||||
const cancelQueueItem = useCallback(async () => {
|
||||
try {
|
||||
await trigger({ item_id }).unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_FAILED',
|
||||
title: t('queue.cancelFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [item_id, t, trigger]);
|
||||
const trigger = useCallback(
|
||||
async (item_id: number) => {
|
||||
try {
|
||||
await _trigger({ item_id }).unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_FAILED',
|
||||
title: t('queue.cancelFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
},
|
||||
[t, _trigger]
|
||||
);
|
||||
|
||||
return { cancelQueueItem, isLoading, isDisabled: !isConnected };
|
||||
return { trigger, isLoading, isDisabled: !isConnected };
|
||||
};
|
||||
|
||||
@@ -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 { useCancelQueueItemsByDestinationMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useCancelQueueItemsByDestination = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const [_trigger, { isLoading }] = useCancelQueueItemsByDestinationMutation();
|
||||
const { t } = useTranslation();
|
||||
const trigger = useCallback(
|
||||
async (destination: string) => {
|
||||
try {
|
||||
await _trigger({ destination }).unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_FAILED',
|
||||
title: t('queue.cancelFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
},
|
||||
[t, _trigger]
|
||||
);
|
||||
|
||||
return { trigger, isLoading, isDisabled: !isConnected };
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useClearInvocationCacheMutation, useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -9,19 +9,13 @@ export const useClearInvocationCache = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useClearInvocationCacheMutation({
|
||||
const [_trigger, { isLoading }] = useClearInvocationCacheMutation({
|
||||
fixedCacheKey: 'clearInvocationCache',
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(() => !cacheStatus?.size || !isConnected, [cacheStatus?.size, isConnected]);
|
||||
|
||||
const clearInvocationCache = useCallback(async () => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = useCallback(async () => {
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'INVOCATION_CACHE_CLEAR_SUCCEEDED',
|
||||
title: t('invocationCache.clearSucceeded'),
|
||||
@@ -34,7 +28,7 @@ export const useClearInvocationCache = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [isDisabled, trigger, t]);
|
||||
}, [_trigger, t]);
|
||||
|
||||
return { clearInvocationCache, isLoading, cacheStatus, isDisabled };
|
||||
return { trigger, isLoading, isDisabled: !isConnected || !cacheStatus?.size };
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { listCursorChanged, listPriorityChanged } from 'features/queue/store/queueSlice';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useClearQueueMutation, useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -12,17 +12,17 @@ export const useClearQueue = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useClearQueueMutation({
|
||||
const [_trigger, { isLoading }] = useClearQueueMutation({
|
||||
fixedCacheKey: 'clearQueue',
|
||||
});
|
||||
|
||||
const clearQueue = useCallback(async () => {
|
||||
const trigger = useCallback(async () => {
|
||||
if (!queueStatus?.queue.total) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CLEAR_SUCCEEDED',
|
||||
title: t('queue.clearSucceeded'),
|
||||
@@ -37,14 +37,7 @@ export const useClearQueue = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [queueStatus?.queue.total, trigger, dispatch, t]);
|
||||
}, [queueStatus?.queue.total, _trigger, dispatch, t]);
|
||||
|
||||
const isDisabled = useMemo(() => !isConnected || !queueStatus?.queue.total, [isConnected, queueStatus?.queue.total]);
|
||||
|
||||
return {
|
||||
clearQueue,
|
||||
isLoading,
|
||||
queueStatus,
|
||||
isDisabled,
|
||||
};
|
||||
return { trigger, isLoading, isDisabled: !isConnected || !queueStatus?.queue.total };
|
||||
};
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { useGetCurrentQueueItemQuery } from 'services/api/endpoints/queue';
|
||||
|
||||
export const useCurrentDestination = () => {
|
||||
const { destination } = useGetCurrentQueueItemQuery(undefined, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
destination: data ? data.destination : null,
|
||||
}),
|
||||
});
|
||||
|
||||
return destination;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
|
||||
export const useCurrentQueueItemId = () => {
|
||||
const { currentQueueItemId } = useGetQueueStatusQuery(undefined, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
currentQueueItemId: data?.queue.item_id ?? null,
|
||||
}),
|
||||
});
|
||||
|
||||
return currentQueueItemId;
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDeleteAllExceptCurrentMutation, useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useDeleteAllExceptCurrentQueueItem = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const isConnected = useStore($isConnected);
|
||||
const [_trigger, { isLoading }] = useDeleteAllExceptCurrentMutation({
|
||||
fixedCacheKey: 'deleteAllExceptCurrent',
|
||||
});
|
||||
|
||||
const trigger = useCallback(async () => {
|
||||
if (!queueStatus?.queue.pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_FAILED',
|
||||
title: t('queue.cancelFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [queueStatus?.queue.pending, _trigger, t]);
|
||||
|
||||
return { trigger, isLoading, isDisabled: !isConnected || !queueStatus?.queue.pending } as const;
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useCurrentQueueItemId } from 'features/queue/hooks/useCurrentQueueItemId';
|
||||
import { useDeleteQueueItem } from 'features/queue/hooks/useDeleteQueueItem';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useDeleteCurrentQueueItem = () => {
|
||||
const currentQueueItemId = useCurrentQueueItemId();
|
||||
const deleteQueueItem = useDeleteQueueItem();
|
||||
const trigger = useCallback(() => {
|
||||
if (currentQueueItemId === null) {
|
||||
return;
|
||||
}
|
||||
deleteQueueItem.trigger(currentQueueItemId);
|
||||
}, [currentQueueItemId, deleteQueueItem]);
|
||||
|
||||
return {
|
||||
trigger,
|
||||
isLoading: deleteQueueItem.isLoading,
|
||||
isDisabled: deleteQueueItem.isDisabled || currentQueueItemId === null,
|
||||
};
|
||||
};
|
||||
@@ -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 { useDeleteQueueItemMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useDeleteQueueItem = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const [_trigger, { isLoading }] = useDeleteQueueItemMutation();
|
||||
const { t } = useTranslation();
|
||||
const trigger = useCallback(
|
||||
async (item_id: number) => {
|
||||
try {
|
||||
await _trigger({ item_id }).unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_FAILED',
|
||||
title: t('queue.cancelFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
},
|
||||
[t, _trigger]
|
||||
);
|
||||
|
||||
return { trigger, isLoading, isDisabled: !isConnected };
|
||||
};
|
||||
@@ -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 { useDeleteQueueItemsByDestinationMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useDeleteQueueItemsByDestination = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const [_trigger, { isLoading }] = useDeleteQueueItemsByDestinationMutation();
|
||||
const { t } = useTranslation();
|
||||
const trigger = useCallback(
|
||||
async (destination: string) => {
|
||||
try {
|
||||
await _trigger({ destination }).unwrap();
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_SUCCEEDED',
|
||||
title: t('queue.cancelSucceeded'),
|
||||
status: 'success',
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
id: 'QUEUE_CANCEL_FAILED',
|
||||
title: t('queue.cancelFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
},
|
||||
[t, _trigger]
|
||||
);
|
||||
|
||||
return { trigger, isLoading, isDisabled: !isConnected };
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDisableInvocationCacheMutation, useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -9,22 +9,13 @@ export const useDisableInvocationCache = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useDisableInvocationCacheMutation({
|
||||
const [_trigger, { isLoading }] = useDisableInvocationCacheMutation({
|
||||
fixedCacheKey: 'disableInvocationCache',
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() => !cacheStatus?.enabled || !isConnected || cacheStatus?.max_size === 0,
|
||||
[cacheStatus?.enabled, cacheStatus?.max_size, isConnected]
|
||||
);
|
||||
|
||||
const disableInvocationCache = useCallback(async () => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = useCallback(async () => {
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'INVOCATION_CACHE_DISABLE_SUCCEEDED',
|
||||
title: t('invocationCache.disableSucceeded'),
|
||||
@@ -37,7 +28,12 @@ export const useDisableInvocationCache = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [isDisabled, trigger, t]);
|
||||
}, [_trigger, t]);
|
||||
|
||||
return { disableInvocationCache, isLoading, cacheStatus, isDisabled };
|
||||
return {
|
||||
trigger,
|
||||
isLoading,
|
||||
cacheStatus,
|
||||
isDisabled: !cacheStatus?.enabled || !isConnected || cacheStatus?.max_size === 0,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useEnableInvocationCacheMutation, useGetInvocationCacheStatusQuery } from 'services/api/endpoints/appInfo';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -9,22 +9,13 @@ export const useEnableInvocationCache = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data: cacheStatus } = useGetInvocationCacheStatusQuery();
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useEnableInvocationCacheMutation({
|
||||
const [_trigger, { isLoading }] = useEnableInvocationCacheMutation({
|
||||
fixedCacheKey: 'enableInvocationCache',
|
||||
});
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() => cacheStatus?.enabled || !isConnected || cacheStatus?.max_size === 0,
|
||||
[cacheStatus?.enabled, cacheStatus?.max_size, isConnected]
|
||||
);
|
||||
|
||||
const enableInvocationCache = useCallback(async () => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = useCallback(async () => {
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'INVOCATION_CACHE_ENABLE_SUCCEEDED',
|
||||
title: t('invocationCache.enableSucceeded'),
|
||||
@@ -37,7 +28,11 @@ export const useEnableInvocationCache = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [isDisabled, trigger, t]);
|
||||
}, [_trigger, t]);
|
||||
|
||||
return { enableInvocationCache, isLoading, cacheStatus, isDisabled };
|
||||
return {
|
||||
trigger,
|
||||
isLoading,
|
||||
isDisabled: cacheStatus?.enabled || !isConnected || cacheStatus?.max_size === 0,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetQueueStatusQuery, usePauseProcessorMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -9,18 +9,13 @@ export const usePauseProcessor = () => {
|
||||
const { t } = useTranslation();
|
||||
const isConnected = useStore($isConnected);
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const [trigger, { isLoading }] = usePauseProcessorMutation({
|
||||
const [_trigger, { isLoading }] = usePauseProcessorMutation({
|
||||
fixedCacheKey: 'pauseProcessor',
|
||||
});
|
||||
|
||||
const isStarted = useMemo(() => Boolean(queueStatus?.processor.is_started), [queueStatus?.processor.is_started]);
|
||||
|
||||
const pauseProcessor = useCallback(async () => {
|
||||
if (!isStarted) {
|
||||
return;
|
||||
}
|
||||
const trigger = useCallback(async () => {
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'PAUSE_SUCCEEDED',
|
||||
title: t('queue.pauseSucceeded'),
|
||||
@@ -33,9 +28,7 @@ export const usePauseProcessor = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [isStarted, trigger, t]);
|
||||
}, [_trigger, t]);
|
||||
|
||||
const isDisabled = useMemo(() => !isConnected || !isStarted, [isConnected, isStarted]);
|
||||
|
||||
return { pauseProcessor, isLoading, isStarted, isDisabled };
|
||||
return { trigger, isLoading, isDisabled: !isConnected || !queueStatus?.processor.is_started };
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { listCursorChanged, listPriorityChanged } from 'features/queue/store/queueSlice';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetQueueStatusQuery, usePruneQueueMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -11,27 +11,14 @@ export const usePruneQueue = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = usePruneQueueMutation({
|
||||
const finishedCount = useFinishedCount();
|
||||
const [_trigger, { isLoading }] = usePruneQueueMutation({
|
||||
fixedCacheKey: 'pruneQueue',
|
||||
});
|
||||
const { finishedCount } = useGetQueueStatusQuery(undefined, {
|
||||
selectFromResult: ({ data }) => {
|
||||
if (!data) {
|
||||
return { finishedCount: 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
finishedCount: data.queue.completed + data.queue.canceled + data.queue.failed,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const pruneQueue = useCallback(async () => {
|
||||
if (!finishedCount) {
|
||||
return;
|
||||
}
|
||||
const trigger = useCallback(async () => {
|
||||
try {
|
||||
const data = await trigger().unwrap();
|
||||
const data = await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'PRUNE_SUCCEEDED',
|
||||
title: t('queue.pruneSucceeded', { item_count: data.deleted }),
|
||||
@@ -46,9 +33,23 @@ export const usePruneQueue = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [finishedCount, trigger, dispatch, t]);
|
||||
}, [_trigger, dispatch, t]);
|
||||
|
||||
const isDisabled = useMemo(() => !isConnected || !finishedCount, [finishedCount, isConnected]);
|
||||
|
||||
return { pruneQueue, isLoading, finishedCount, isDisabled };
|
||||
return { trigger, isLoading, isDisabled: !isConnected || !finishedCount };
|
||||
};
|
||||
|
||||
export const useFinishedCount = () => {
|
||||
const { finishedCount } = useGetQueueStatusQuery(undefined, {
|
||||
selectFromResult: ({ data }) => {
|
||||
if (!data) {
|
||||
return { finishedCount: 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
finishedCount: data.queue.completed + data.queue.canceled + data.queue.failed,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
return finishedCount;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetQueueStatusQuery, useResumeProcessorMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
@@ -9,18 +9,13 @@ export const useResumeProcessor = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const { t } = useTranslation();
|
||||
const [trigger, { isLoading }] = useResumeProcessorMutation({
|
||||
const [_trigger, { isLoading }] = useResumeProcessorMutation({
|
||||
fixedCacheKey: 'resumeProcessor',
|
||||
});
|
||||
|
||||
const isStarted = useMemo(() => Boolean(queueStatus?.processor.is_started), [queueStatus?.processor.is_started]);
|
||||
|
||||
const resumeProcessor = useCallback(async () => {
|
||||
if (isStarted) {
|
||||
return;
|
||||
}
|
||||
const trigger = useCallback(async () => {
|
||||
try {
|
||||
await trigger().unwrap();
|
||||
await _trigger().unwrap();
|
||||
toast({
|
||||
id: 'PROCESSOR_RESUMED',
|
||||
title: t('queue.resumeSucceeded'),
|
||||
@@ -33,9 +28,7 @@ export const useResumeProcessor = () => {
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
}, [isStarted, trigger, t]);
|
||||
}, [_trigger, t]);
|
||||
|
||||
const isDisabled = useMemo(() => !isConnected || isStarted, [isConnected, isStarted]);
|
||||
|
||||
return { resumeProcessor, isLoading, isStarted, isDisabled };
|
||||
return { trigger, isLoading, isDisabled: !isConnected || !queueStatus?.processor.is_started };
|
||||
};
|
||||
|
||||
@@ -5,29 +5,32 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useRetryItemsByIdMutation } from 'services/api/endpoints/queue';
|
||||
import { $isConnected } from 'services/events/stores';
|
||||
|
||||
export const useRetryQueueItem = (item_id: number) => {
|
||||
export const useRetryQueueItem = () => {
|
||||
const isConnected = useStore($isConnected);
|
||||
const [trigger, { isLoading }] = useRetryItemsByIdMutation();
|
||||
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');
|
||||
const trigger = useCallback(
|
||||
async (item_id: number) => {
|
||||
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',
|
||||
});
|
||||
}
|
||||
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]);
|
||||
},
|
||||
[t, _trigger]
|
||||
);
|
||||
|
||||
return { retryQueueItem, isLoading, isDisabled: !isConnected };
|
||||
return { trigger, isLoading, isDisabled: !isConnected };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user