mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-12 21:14:59 -05:00
refactor(ui): migrate from canceling queue items to deleteing, make queue hook APIs consistent
This commit is contained in:
@@ -14,6 +14,7 @@ from invokeai.app.services.session_queue.session_queue_common import (
|
||||
CancelByBatchIDsResult,
|
||||
CancelByDestinationResult,
|
||||
ClearResult,
|
||||
DeleteAllExceptCurrentResult,
|
||||
DeleteByDestinationResult,
|
||||
EnqueueBatchResult,
|
||||
FieldIdentifier,
|
||||
@@ -146,6 +147,18 @@ async def cancel_all_except_current(
|
||||
return ApiDependencies.invoker.services.session_queue.cancel_all_except_current(queue_id=queue_id)
|
||||
|
||||
|
||||
@session_queue_router.put(
|
||||
"/{queue_id}/delete_all_except_current",
|
||||
operation_id="delete_all_except_current",
|
||||
responses={200: {"model": DeleteAllExceptCurrentResult}},
|
||||
)
|
||||
async def delete_all_except_current(
|
||||
queue_id: str = Path(description="The queue id to perform this operation on"),
|
||||
) -> DeleteAllExceptCurrentResult:
|
||||
"""Immediately deletes all queue items except in-processing items"""
|
||||
return ApiDependencies.invoker.services.session_queue.delete_all_except_current(queue_id=queue_id)
|
||||
|
||||
|
||||
@session_queue_router.put(
|
||||
"/{queue_id}/cancel_by_batch_ids",
|
||||
operation_id="cancel_by_batch_ids",
|
||||
|
||||
@@ -10,6 +10,7 @@ from invokeai.app.services.session_queue.session_queue_common import (
|
||||
CancelByDestinationResult,
|
||||
CancelByQueueIDResult,
|
||||
ClearResult,
|
||||
DeleteAllExceptCurrentResult,
|
||||
DeleteByDestinationResult,
|
||||
EnqueueBatchResult,
|
||||
IsEmptyResult,
|
||||
@@ -129,6 +130,11 @@ class SessionQueueBase(ABC):
|
||||
"""Cancels all queue items except in-progress items"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete_all_except_current(self, queue_id: str) -> DeleteAllExceptCurrentResult:
|
||||
"""Deletes all queue items except in-progress items"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_queue_items(
|
||||
self,
|
||||
|
||||
@@ -369,6 +369,12 @@ class DeleteByDestinationResult(BaseModel):
|
||||
deleted: int = Field(..., description="Number of queue items deleted")
|
||||
|
||||
|
||||
class DeleteAllExceptCurrentResult(DeleteByDestinationResult):
|
||||
"""Result of deleting all except current"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CancelByQueueIDResult(CancelByBatchIDsResult):
|
||||
"""Result of canceling by queue id"""
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from invokeai.app.services.session_queue.session_queue_common import (
|
||||
CancelByDestinationResult,
|
||||
CancelByQueueIDResult,
|
||||
ClearResult,
|
||||
DeleteAllExceptCurrentResult,
|
||||
DeleteByDestinationResult,
|
||||
EnqueueBatchResult,
|
||||
IsEmptyResult,
|
||||
@@ -489,6 +490,37 @@ class SqliteSessionQueue(SessionQueueBase):
|
||||
raise
|
||||
return DeleteByDestinationResult(deleted=count)
|
||||
|
||||
def delete_all_except_current(self, queue_id: str) -> DeleteAllExceptCurrentResult:
|
||||
try:
|
||||
cursor = self._conn.cursor()
|
||||
where = """--sql
|
||||
WHERE
|
||||
queue_id == ?
|
||||
AND status == 'pending'
|
||||
"""
|
||||
cursor.execute(
|
||||
f"""--sql
|
||||
SELECT COUNT(*)
|
||||
FROM session_queue
|
||||
{where};
|
||||
""",
|
||||
(queue_id,),
|
||||
)
|
||||
count = cursor.fetchone()[0]
|
||||
cursor.execute(
|
||||
f"""--sql
|
||||
DELETE
|
||||
FROM session_queue
|
||||
{where};
|
||||
""",
|
||||
(queue_id,),
|
||||
)
|
||||
self._conn.commit()
|
||||
except Exception:
|
||||
self._conn.rollback()
|
||||
raise
|
||||
return DeleteAllExceptCurrentResult(deleted=count)
|
||||
|
||||
def cancel_by_queue_id(self, queue_id: str) -> CancelByQueueIDResult:
|
||||
try:
|
||||
cursor = self._conn.cursor()
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ShareWorkflowModal } from 'features/nodes/components/sidePanel/workflow
|
||||
import { WorkflowLibraryModal } from 'features/nodes/components/sidePanel/workflow/WorkflowLibrary/WorkflowLibraryModal';
|
||||
import { CancelAllExceptCurrentQueueItemConfirmationAlertDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { ClearQueueConfirmationsAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
|
||||
import { DeleteAllExceptCurrentQueueItemConfirmationAlertDialog } from 'features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { DeleteStylePresetDialog } from 'features/stylePresets/components/DeleteStylePresetDialog';
|
||||
import { StylePresetModal } from 'features/stylePresets/components/StylePresetForm/StylePresetModal';
|
||||
import RefreshAfterResetModal from 'features/system/components/SettingsModal/RefreshAfterResetModal';
|
||||
@@ -40,6 +41,7 @@ export const GlobalModalIsolator = memo(() => {
|
||||
<StylePresetModal />
|
||||
<WorkflowLibraryModal />
|
||||
<CancelAllExceptCurrentQueueItemConfirmationAlertDialog />
|
||||
<DeleteAllExceptCurrentQueueItemConfirmationAlertDialog />
|
||||
<ClearQueueConfirmationsAlertDialog />
|
||||
<NewWorkflowConfirmationAlertDialog />
|
||||
<LoadWorkflowConfirmationAlertDialog />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem';
|
||||
import { useClearQueue } from 'features/queue/hooks/useClearQueue';
|
||||
import { useDeleteCurrentQueueItem } from 'features/queue/hooks/useDeleteCurrentQueueItem';
|
||||
import { useInvoke } from 'features/queue/hooks/useInvoke';
|
||||
import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
@@ -35,34 +35,30 @@ export const useGlobalHotkeys = () => {
|
||||
dependencies: [queue],
|
||||
});
|
||||
|
||||
const {
|
||||
cancelQueueItem,
|
||||
isDisabled: isDisabledCancelQueueItem,
|
||||
isLoading: isLoadingCancelQueueItem,
|
||||
} = useCancelCurrentQueueItem();
|
||||
const deleteCurrentQueueItem = useDeleteCurrentQueueItem();
|
||||
|
||||
useRegisteredHotkeys({
|
||||
id: 'cancelQueueItem',
|
||||
category: 'app',
|
||||
callback: cancelQueueItem,
|
||||
callback: deleteCurrentQueueItem.trigger,
|
||||
options: {
|
||||
enabled: !isDisabledCancelQueueItem && !isLoadingCancelQueueItem,
|
||||
enabled: !deleteCurrentQueueItem.isDisabled && !deleteCurrentQueueItem.isLoading,
|
||||
preventDefault: true,
|
||||
},
|
||||
dependencies: [cancelQueueItem, isDisabledCancelQueueItem, isLoadingCancelQueueItem],
|
||||
dependencies: [deleteCurrentQueueItem],
|
||||
});
|
||||
|
||||
const { clearQueue, isDisabled: isDisabledClearQueue, isLoading: isLoadingClearQueue } = useClearQueue();
|
||||
const clearQueue = useClearQueue();
|
||||
|
||||
useRegisteredHotkeys({
|
||||
id: 'clearQueue',
|
||||
category: 'app',
|
||||
callback: clearQueue,
|
||||
callback: clearQueue.trigger,
|
||||
options: {
|
||||
enabled: !isDisabledClearQueue && !isLoadingClearQueue,
|
||||
enabled: !clearQueue.isDisabled && !clearQueue.isLoading,
|
||||
preventDefault: true,
|
||||
},
|
||||
dependencies: [clearQueue, isDisabledClearQueue, isLoadingClearQueue],
|
||||
dependencies: [clearQueue],
|
||||
});
|
||||
|
||||
useRegisteredHotkeys({
|
||||
|
||||
@@ -67,34 +67,6 @@ const setProgress = ($progressData: WritableAtom<Record<number, ProgressData>>,
|
||||
}
|
||||
};
|
||||
|
||||
const clearProgressEvent = ($progressData: WritableAtom<Record<number, ProgressData>>, itemId: number) => {
|
||||
const progressData = $progressData.get();
|
||||
const current = progressData[itemId];
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
const next = { ...current };
|
||||
next.progressEvent = null;
|
||||
$progressData.set({
|
||||
...progressData,
|
||||
[itemId]: next,
|
||||
});
|
||||
};
|
||||
|
||||
const clearProgressImage = ($progressData: WritableAtom<Record<number, ProgressData>>, itemId: number) => {
|
||||
const progressData = $progressData.get();
|
||||
const current = progressData[itemId];
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
const next = { ...current };
|
||||
next.progressImage = null;
|
||||
$progressData.set({
|
||||
...progressData,
|
||||
[itemId]: next,
|
||||
});
|
||||
};
|
||||
|
||||
type CanvasSessionContextValue = {
|
||||
session: { id: string; type: 'simple' | 'advanced' };
|
||||
$items: Atom<S['SessionQueueItem'][]>;
|
||||
@@ -132,15 +104,11 @@ export const CanvasSessionContextProvider = memo(
|
||||
const socket = useStore($socket);
|
||||
|
||||
/**
|
||||
* Manually-synced atom containing the queue items for the current session.
|
||||
* Manually-synced atom containing queue items for the current session. This is populated from the RTK Query cache
|
||||
* and kept in sync with it via a redux subscription.
|
||||
*/
|
||||
const $items = useState(() => atom<S['SessionQueueItem'][]>([]))[0];
|
||||
|
||||
/**
|
||||
* Manually-synced atom containing the queue items for the current session.
|
||||
*/
|
||||
const $prevItems = useState(() => atom<S['SessionQueueItem'][]>([]))[0];
|
||||
|
||||
/**
|
||||
* Whether auto-switch is enabled.
|
||||
*/
|
||||
@@ -294,28 +262,16 @@ export const CanvasSessionContextProvider = memo(
|
||||
setProgress($progressData, data);
|
||||
};
|
||||
|
||||
const onQueueItemStatusChanged = (data: S['QueueItemStatusChangedEvent']) => {
|
||||
if (data.destination !== session.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.status === 'canceled' || data.status === 'failed') {
|
||||
clearProgressEvent($progressData, data.item_id);
|
||||
clearProgressImage($progressData, data.item_id);
|
||||
}
|
||||
};
|
||||
|
||||
socket.on('invocation_progress', onProgress);
|
||||
socket.on('queue_item_status_changed', onQueueItemStatusChanged);
|
||||
|
||||
return () => {
|
||||
socket.off('invocation_progress', onProgress);
|
||||
socket.off('queue_item_status_changed', onQueueItemStatusChanged);
|
||||
};
|
||||
}, [$autoSwitch, $progressData, $selectedItemId, session.id, socket]);
|
||||
|
||||
// Set up state subscriptions and effects
|
||||
useEffect(() => {
|
||||
let _prevItems: readonly S['SessionQueueItem'][] = [];
|
||||
// Seed the $items atom with the initial query cache state
|
||||
$items.set(selectQueueItems(store.getState()));
|
||||
|
||||
@@ -324,7 +280,7 @@ export const CanvasSessionContextProvider = memo(
|
||||
const prevItems = $items.get();
|
||||
const items = selectQueueItems(store.getState());
|
||||
if (items !== prevItems) {
|
||||
$prevItems.set(prevItems);
|
||||
_prevItems = prevItems;
|
||||
$items.set(items);
|
||||
}
|
||||
});
|
||||
@@ -344,7 +300,7 @@ export const CanvasSessionContextProvider = memo(
|
||||
// If an item is selected and it is not in the list of items, un-set it. This effect will run again and we'll
|
||||
// the above case, selecting the first item if there are any.
|
||||
if (selectedItemId !== null && items.findIndex(({ item_id }) => item_id === selectedItemId) === -1) {
|
||||
let prevIndex = $prevItems.get().findIndex(({ item_id }) => item_id === selectedItemId);
|
||||
let prevIndex = _prevItems.findIndex(({ item_id }) => item_id === selectedItemId);
|
||||
if (prevIndex >= items.length) {
|
||||
prevIndex = items.length - 1;
|
||||
}
|
||||
@@ -357,19 +313,37 @@ export const CanvasSessionContextProvider = memo(
|
||||
// Clean up the progress data when a queue item is discarded.
|
||||
const unsubCleanUpProgressData = $items.listen((items) => {
|
||||
const progressData = $progressData.get();
|
||||
|
||||
const toDelete: number[] = [];
|
||||
const toClear: number[] = [];
|
||||
|
||||
for (const datum of Object.values(progressData)) {
|
||||
if (items.findIndex(({ item_id }) => item_id === datum.itemId) === -1) {
|
||||
const item = items.find(({ item_id }) => item_id === datum.itemId);
|
||||
if (!item) {
|
||||
toDelete.push(datum.itemId);
|
||||
} else if (item.status === 'canceled' || item.status === 'failed') {
|
||||
toClear.push(datum.itemId);
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newProgressData = { ...progressData };
|
||||
|
||||
for (const itemId of toDelete) {
|
||||
delete newProgressData[itemId];
|
||||
}
|
||||
|
||||
for (const itemId of toClear) {
|
||||
const current = newProgressData[itemId];
|
||||
if (current) {
|
||||
current.progressEvent = null;
|
||||
current.progressImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
$progressData.set(newProgressData);
|
||||
});
|
||||
|
||||
@@ -408,17 +382,7 @@ export const CanvasSessionContextProvider = memo(
|
||||
$progressData.set({});
|
||||
$selectedItemId.set(null);
|
||||
};
|
||||
}, [
|
||||
$autoSwitch,
|
||||
$items,
|
||||
$lastLoadedItemId,
|
||||
$prevItems,
|
||||
$progressData,
|
||||
$selectedItemId,
|
||||
selectQueueItems,
|
||||
session.id,
|
||||
store,
|
||||
]);
|
||||
}, [$autoSwitch, $items, $lastLoadedItemId, $progressData, $selectedItemId, selectQueueItems, session.id, store]);
|
||||
|
||||
const value = useMemo<CanvasSessionContextValue>(
|
||||
() => ({
|
||||
|
||||
@@ -9,11 +9,11 @@ import { canvasSessionGenerationFinished } from 'features/controlLayers/store/ca
|
||||
import { selectBboxRect, selectSelectedEntityIdentifier } from 'features/controlLayers/store/selectors';
|
||||
import type { CanvasRasterLayerState } from 'features/controlLayers/store/types';
|
||||
import { imageNameToImageObject } from 'features/controlLayers/store/util';
|
||||
import { useDeleteQueueItemsByDestination } from 'features/queue/hooks/useDeleteQueueItemsByDestination';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiCheckBold } from 'react-icons/pi';
|
||||
import { useDeleteQueueItemsByDestinationMutation } from 'services/api/endpoints/queue';
|
||||
|
||||
export const StagingAreaToolbarAcceptButton = memo(() => {
|
||||
const ctx = useCanvasSessionContext();
|
||||
@@ -24,7 +24,7 @@ export const StagingAreaToolbarAcceptButton = memo(() => {
|
||||
const shouldShowStagedImage = useStore(canvasManager.stagingArea.$shouldShowStagedImage);
|
||||
const isCanvasFocused = useIsRegionFocused('canvas');
|
||||
const selectedItemImageName = useStore(ctx.$selectedItemOutputImageName);
|
||||
const [deleteByDestination] = useDeleteQueueItemsByDestinationMutation();
|
||||
const deleteQueueItemsByDestination = useDeleteQueueItemsByDestination();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -41,8 +41,15 @@ export const StagingAreaToolbarAcceptButton = memo(() => {
|
||||
|
||||
dispatch(rasterLayerAdded({ overrides, isSelected: selectedEntityIdentifier?.type === 'raster_layer' }));
|
||||
dispatch(canvasSessionGenerationFinished());
|
||||
deleteByDestination({ destination: ctx.session.id });
|
||||
}, [selectedItemImageName, bboxRect, dispatch, selectedEntityIdentifier?.type, deleteByDestination, ctx.session.id]);
|
||||
deleteQueueItemsByDestination.trigger(ctx.session.id);
|
||||
}, [
|
||||
selectedItemImageName,
|
||||
bboxRect,
|
||||
dispatch,
|
||||
selectedEntityIdentifier?.type,
|
||||
deleteQueueItemsByDestination,
|
||||
ctx.session.id,
|
||||
]);
|
||||
|
||||
useHotkeys(
|
||||
['enter'],
|
||||
@@ -61,7 +68,8 @@ export const StagingAreaToolbarAcceptButton = memo(() => {
|
||||
icon={<PiCheckBold />}
|
||||
onClick={acceptSelected}
|
||||
colorScheme="invokeBlue"
|
||||
isDisabled={!selectedItemImageName || !shouldShowStagedImage}
|
||||
isDisabled={!selectedItemImageName || !shouldShowStagedImage || deleteQueueItemsByDestination.isDisabled}
|
||||
isLoading={deleteQueueItemsByDestination.isLoading}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,21 +2,21 @@ import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useCanvasSessionContext } from 'features/controlLayers/components/SimpleSession/context';
|
||||
import { canvasSessionGenerationFinished } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { useDeleteQueueItemsByDestination } from 'features/queue/hooks/useDeleteQueueItemsByDestination';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||
import { useDeleteQueueItemsByDestinationMutation } from 'services/api/endpoints/queue';
|
||||
|
||||
export const StagingAreaToolbarDiscardAllButton = memo(({ isDisabled }: { isDisabled?: boolean }) => {
|
||||
const ctx = useCanvasSessionContext();
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const [deleteByDestination] = useDeleteQueueItemsByDestinationMutation();
|
||||
const deleteQueueItemsByDestination = useDeleteQueueItemsByDestination();
|
||||
|
||||
const discardAll = useCallback(() => {
|
||||
deleteByDestination({ destination: ctx.session.id });
|
||||
deleteQueueItemsByDestination.trigger(ctx.session.id);
|
||||
dispatch(canvasSessionGenerationFinished());
|
||||
}, [deleteByDestination, ctx.session.id, dispatch]);
|
||||
}, [deleteQueueItemsByDestination, ctx.session.id, dispatch]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
@@ -26,7 +26,8 @@ export const StagingAreaToolbarDiscardAllButton = memo(({ isDisabled }: { isDisa
|
||||
onClick={discardAll}
|
||||
colorScheme="error"
|
||||
fontSize={16}
|
||||
isDisabled={isDisabled}
|
||||
isDisabled={isDisabled || deleteQueueItemsByDestination.isDisabled}
|
||||
isLoading={deleteQueueItemsByDestination.isLoading}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,15 +3,15 @@ import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useCanvasSessionContext } from 'features/controlLayers/components/SimpleSession/context';
|
||||
import { canvasSessionGenerationFinished } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { useDeleteQueueItem } from 'features/queue/hooks/useDeleteQueueItem';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
import { useDeleteQueueItemMutation } from 'services/api/endpoints/queue';
|
||||
|
||||
export const StagingAreaToolbarDiscardSelectedButton = memo(({ isDisabled }: { isDisabled?: boolean }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const ctx = useCanvasSessionContext();
|
||||
const [deleteQueueItem] = useDeleteQueueItemMutation();
|
||||
const deleteQueueItem = useDeleteQueueItem();
|
||||
const selectedItemId = useStore(ctx.$selectedItemId);
|
||||
|
||||
const { t } = useTranslation();
|
||||
@@ -21,10 +21,10 @@ export const StagingAreaToolbarDiscardSelectedButton = memo(({ isDisabled }: { i
|
||||
return;
|
||||
}
|
||||
const itemCount = ctx.$itemCount.get();
|
||||
deleteQueueItem({ item_id: selectedItemId });
|
||||
if (itemCount <= 1) {
|
||||
dispatch(canvasSessionGenerationFinished());
|
||||
}
|
||||
deleteQueueItem.trigger(selectedItemId);
|
||||
}, [selectedItemId, ctx.$itemCount, deleteQueueItem, dispatch]);
|
||||
|
||||
return (
|
||||
@@ -35,7 +35,8 @@ export const StagingAreaToolbarDiscardSelectedButton = memo(({ isDisabled }: { i
|
||||
onClick={discardSelected}
|
||||
colorScheme="invokeBlue"
|
||||
fontSize={16}
|
||||
isDisabled={selectedItemId === null || isDisabled}
|
||||
isDisabled={selectedItemId === null || deleteQueueItem.isDisabled || isDisabled}
|
||||
isLoading={deleteQueueItem.isLoading}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Progress } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useCurrentDestination } from 'features/queue/hooks/useCurrentDestination';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
@@ -8,7 +7,6 @@ import { $isConnected, $lastProgressEvent } from 'services/events/stores';
|
||||
|
||||
const ProgressBar = () => {
|
||||
const { t } = useTranslation();
|
||||
const destination = useCurrentDestination();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
const isConnected = useStore($isConnected);
|
||||
const lastProgressEvent = useStore($lastProgressEvent);
|
||||
@@ -39,16 +37,6 @@ const ProgressBar = () => {
|
||||
return false;
|
||||
}, [isConnected, lastProgressEvent, queueStatus?.queue.in_progress]);
|
||||
|
||||
const colorScheme = useMemo(() => {
|
||||
if (destination === 'canvas') {
|
||||
return 'invokeGreen';
|
||||
} else if (destination === 'gallery') {
|
||||
return 'invokeBlue';
|
||||
} else {
|
||||
return 'base';
|
||||
}
|
||||
}, [destination]);
|
||||
|
||||
return (
|
||||
<Progress
|
||||
value={value}
|
||||
@@ -56,7 +44,7 @@ const ProgressBar = () => {
|
||||
isIndeterminate={isIndeterminate}
|
||||
h={2}
|
||||
w="full"
|
||||
colorScheme={colorScheme}
|
||||
colorScheme="invokeBlue"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,9 +3,9 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { selectCanvasSessionType } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { useCancelAllExceptCurrentQueueItemDialog } from 'features/queue/components/CancelAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { useDeleteAllExceptCurrentQueueItemDialog } from 'features/queue/components/DeleteAllExceptCurrentQueueItemConfirmationAlertDialog';
|
||||
import { InvokeButtonTooltip } from 'features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip';
|
||||
import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem';
|
||||
import { useDeleteCurrentQueueItem } from 'features/queue/hooks/useDeleteCurrentQueueItem';
|
||||
import { useInvoke } from 'features/queue/hooks/useInvoke';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { memo } from 'react';
|
||||
@@ -34,8 +34,8 @@ export const FloatingLeftPanelButtons = memo((props: { onToggle: () => void }) =
|
||||
<ButtonGroup orientation="vertical" h={48}>
|
||||
<ToggleLeftPanelButton onToggle={props.onToggle} />
|
||||
<InvokeIconButton />
|
||||
<CancelCurrentIconButton />
|
||||
<CancelAllExceptCurrentIconButton />
|
||||
<DeleteCurrentIconButton />
|
||||
<DeleteAllExceptCurrentIconButton />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
);
|
||||
@@ -103,18 +103,18 @@ const InvokeIconButtonIcon = memo(() => {
|
||||
});
|
||||
InvokeIconButtonIcon.displayName = 'InvokeIconButtonIcon';
|
||||
|
||||
const CancelCurrentIconButton = memo(() => {
|
||||
const DeleteCurrentIconButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const cancelCurrentQueueItem = useCancelCurrentQueueItem();
|
||||
const deleteCurrentQueueItem = useDeleteCurrentQueueItem();
|
||||
|
||||
return (
|
||||
<Tooltip label={t('queue.cancelTooltip')} placement="end">
|
||||
<IconButton
|
||||
isDisabled={cancelCurrentQueueItem.isDisabled}
|
||||
isLoading={cancelCurrentQueueItem.isLoading}
|
||||
onClick={deleteCurrentQueueItem.trigger}
|
||||
isDisabled={deleteCurrentQueueItem.isDisabled}
|
||||
isLoading={deleteCurrentQueueItem.isLoading}
|
||||
aria-label={t('queue.cancelTooltip')}
|
||||
icon={<PiXBold />}
|
||||
onClick={cancelCurrentQueueItem.cancelQueueItem}
|
||||
colorScheme="error"
|
||||
flexGrow={1}
|
||||
/>
|
||||
@@ -122,25 +122,25 @@ const CancelCurrentIconButton = memo(() => {
|
||||
);
|
||||
});
|
||||
|
||||
CancelCurrentIconButton.displayName = 'CancelCurrentIconButton';
|
||||
DeleteCurrentIconButton.displayName = 'DeleteCurrentIconButton';
|
||||
|
||||
const CancelAllExceptCurrentIconButton = memo(() => {
|
||||
const DeleteAllExceptCurrentIconButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const cancelAllExceptCurrent = useCancelAllExceptCurrentQueueItemDialog();
|
||||
const deleteAllExceptCurrent = useDeleteAllExceptCurrentQueueItemDialog();
|
||||
|
||||
return (
|
||||
<Tooltip label={t('queue.cancelAllExceptCurrentTooltip')} placement="end">
|
||||
<IconButton
|
||||
isDisabled={cancelAllExceptCurrent.isDisabled}
|
||||
isLoading={cancelAllExceptCurrent.isLoading}
|
||||
isDisabled={deleteAllExceptCurrent.isDisabled}
|
||||
isLoading={deleteAllExceptCurrent.isLoading}
|
||||
aria-label={t('queue.clear')}
|
||||
icon={<PiXCircle />}
|
||||
colorScheme="error"
|
||||
onClick={cancelAllExceptCurrent.openDialog}
|
||||
onClick={deleteAllExceptCurrent.openDialog}
|
||||
flexGrow={1}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
CancelAllExceptCurrentIconButton.displayName = 'CancelAllExceptCurrentIconButton';
|
||||
DeleteAllExceptCurrentIconButton.displayName = 'DeleteAllExceptCurrentIconButton';
|
||||
|
||||
@@ -224,7 +224,7 @@ export const queueApi = api.injectEndpoints({
|
||||
];
|
||||
},
|
||||
}),
|
||||
cancelByDestination: build.mutation<
|
||||
cancelQueueItemsByDestination: build.mutation<
|
||||
paths['/api/v1/queue/{queue_id}/cancel_by_destination']['put']['responses']['200']['content']['application/json'],
|
||||
paths['/api/v1/queue/{queue_id}/cancel_by_destination']['put']['parameters']['query']
|
||||
>({
|
||||
@@ -256,6 +256,16 @@ export const queueApi = api.injectEndpoints({
|
||||
}),
|
||||
invalidatesTags: ['SessionQueueStatus', 'BatchStatus', 'QueueCountsByDestination', 'SessionQueueItem'],
|
||||
}),
|
||||
deleteAllExceptCurrent: build.mutation<
|
||||
paths['/api/v1/queue/{queue_id}/delete_all_except_current']['put']['responses']['200']['content']['application/json'],
|
||||
void
|
||||
>({
|
||||
query: () => ({
|
||||
url: buildQueueUrl('delete_all_except_current'),
|
||||
method: 'PUT',
|
||||
}),
|
||||
invalidatesTags: ['SessionQueueStatus', 'BatchStatus', 'QueueCountsByDestination', 'SessionQueueItem'],
|
||||
}),
|
||||
retryItemsById: build.mutation<
|
||||
paths['/api/v1/queue/{queue_id}/retry_items_by_id']['put']['responses']['200']['content']['application/json'],
|
||||
paths['/api/v1/queue/{queue_id}/retry_items_by_id']['put']['requestBody']['content']['application/json']
|
||||
@@ -329,7 +339,11 @@ export const queueApi = api.injectEndpoints({
|
||||
url: buildQueueUrl(`i/${item_id}`),
|
||||
method: 'DELETE',
|
||||
}),
|
||||
invalidatesTags: (result, error, { item_id }) => [{ type: 'SessionQueueItem', id: item_id }],
|
||||
invalidatesTags: (result, error, { item_id }) => [
|
||||
{ type: 'SessionQueueItem', id: item_id },
|
||||
{ type: 'SessionQueueItem', id: LIST_TAG },
|
||||
{ type: 'SessionQueueItem', id: LIST_ALL_TAG },
|
||||
],
|
||||
}),
|
||||
deleteQueueItemsByDestination: build.mutation<void, { destination: string }>({
|
||||
query: ({ destination }) => ({
|
||||
@@ -366,8 +380,10 @@ export const {
|
||||
useGetQueueStatusQuery,
|
||||
useListQueueItemsQuery,
|
||||
useCancelQueueItemMutation,
|
||||
useCancelQueueItemsByDestinationMutation,
|
||||
useDeleteQueueItemMutation,
|
||||
useDeleteQueueItemsByDestinationMutation,
|
||||
useDeleteAllExceptCurrentMutation,
|
||||
useGetBatchStatusQuery,
|
||||
useGetCurrentQueueItemQuery,
|
||||
useGetQueueCountsByDestinationQuery,
|
||||
|
||||
@@ -1244,6 +1244,26 @@ export type paths = {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v1/queue/{queue_id}/delete_all_except_current": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
/**
|
||||
* Delete All Except Current
|
||||
* @description Immediately deletes all queue items except in-processing items
|
||||
*/
|
||||
put: operations["delete_all_except_current"];
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v1/queue/{queue_id}/cancel_by_batch_ids": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -5885,6 +5905,17 @@ export type components = {
|
||||
*/
|
||||
type: "dw_openpose_detection";
|
||||
};
|
||||
/**
|
||||
* DeleteAllExceptCurrentResult
|
||||
* @description Result of deleting all except current
|
||||
*/
|
||||
DeleteAllExceptCurrentResult: {
|
||||
/**
|
||||
* Deleted
|
||||
* @description Number of queue items deleted
|
||||
*/
|
||||
deleted: number;
|
||||
};
|
||||
/** DeleteBoardResult */
|
||||
DeleteBoardResult: {
|
||||
/**
|
||||
@@ -24570,6 +24601,38 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
delete_all_except_current: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description The queue id to perform this operation on */
|
||||
queue_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["DeleteAllExceptCurrentResult"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
cancel_by_batch_ids: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
Reference in New Issue
Block a user