feat(ui): split out delete workflow dialog logic into singleton

This commit is contained in:
psychedelicious
2024-10-10 12:53:41 +10:00
parent 5d6a2a3709
commit 2c601438eb
4 changed files with 65 additions and 74 deletions

View File

@@ -27,6 +27,7 @@ import RefreshAfterResetModal from 'features/system/components/SettingsModal/Ref
import { configChanged } from 'features/system/store/configSlice';
import { selectLanguage } from 'features/system/store/systemSelectors';
import { AppContent } from 'features/ui/components/AppContent';
import { DeleteWorkflowDialog } from 'features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog';
import { NewWorkflowConfirmationAlertDialog } from 'features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog';
import { AnimatePresence } from 'framer-motion';
import i18n from 'i18n';
@@ -109,6 +110,7 @@ const App = ({ config = DEFAULT_CONFIG, studioInitAction }: Props) => {
<StylePresetModal />
<ClearQueueConfirmationsAlertDialog />
<NewWorkflowConfirmationAlertDialog />
<DeleteWorkflowDialog />
<RefreshAfterResetModal />
<DeleteBoardModal />
<GlobalImageHotkeys />

View File

@@ -1,13 +1,12 @@
import { Badge, ConfirmationAlertDialog, Flex, IconButton, Spacer, Text, Tooltip } from '@invoke-ai/ui-library';
import { Badge, Flex, IconButton, Spacer, Text, Tooltip } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { EMPTY_OBJECT } from 'app/store/constants';
import { $projectUrl } from 'app/store/nanostores/projectId';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useDisclosure } from 'common/hooks/useBoolean';
import dateFormat, { masks } from 'dateformat';
import { useWorkflowListMenu } from 'features/nodes/store/workflowListMenu';
import { selectWorkflowId, workflowModeChanged } from 'features/nodes/store/workflowSlice';
import { useDeleteLibraryWorkflow } from 'features/workflowLibrary/hooks/useDeleteLibraryWorkflow';
import { useDeleteWorkflow } from 'features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog';
import { useDownloadWorkflow } from 'features/workflowLibrary/hooks/useDownloadWorkflow';
import { useGetAndLoadLibraryWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow';
import type { MouseEvent } from 'react';
@@ -20,7 +19,6 @@ import { CopyWorkflowLinkModal } from './CopyWorkflowLinkModal';
import { WorkflowListItemTooltip } from './WorkflowListItemTooltip';
export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListItemDTO }) => {
const deleteConfirmationDialog = useDisclosure(false);
const copyWorkflowLinkModal = useDisclosure(false);
const { t } = useTranslation();
const dispatch = useAppDispatch();
@@ -39,7 +37,7 @@ export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListIte
const workflowId = useAppSelector(selectWorkflowId);
const downloadWorkflow = useDownloadWorkflow();
const { deleteWorkflow, deleteWorkflowResult } = useDeleteLibraryWorkflow(EMPTY_OBJECT);
const deleteWorkflow = useDeleteWorkflow();
const { getAndLoadWorkflow } = useGetAndLoadLibraryWorkflow({
onSuccess: workflowListMenu.close,
});
@@ -68,23 +66,13 @@ export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListIte
[getAndLoadWorkflow, workflow.workflow_id, dispatch, workflowListMenu]
);
const handleDeleteWorklow = useCallback(
(e?: MouseEvent<HTMLButtonElement>) => {
e?.stopPropagation();
setIsHovered(false);
deleteWorkflow(workflow.workflow_id);
deleteConfirmationDialog.close();
},
[deleteWorkflow, workflow.workflow_id, deleteConfirmationDialog]
);
const handleClickDelete = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setIsHovered(false);
deleteConfirmationDialog.open();
deleteWorkflow(workflow);
},
[deleteConfirmationDialog]
[deleteWorkflow, workflow]
);
const handleClickShare = useCallback(
@@ -158,7 +146,6 @@ export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListIte
variant="ghost"
aria-label={t('workflows.edit')}
onClick={handleClickEdit}
isLoading={deleteWorkflowResult.isLoading}
icon={<PiPencilBold />}
/>
</Tooltip>
@@ -201,7 +188,6 @@ export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListIte
variant="ghost"
aria-label={t('workflows.delete')}
onClick={handleClickDelete}
isLoading={deleteWorkflowResult.isLoading}
colorScheme="error"
icon={<PiTrashBold />}
/>
@@ -209,17 +195,6 @@ export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListIte
)}
</Flex>
</Flex>
<ConfirmationAlertDialog
isOpen={deleteConfirmationDialog.isOpen}
onClose={deleteConfirmationDialog.close}
title={t('workflows.deleteWorkflow')}
acceptCallback={handleDeleteWorklow}
acceptButtonText={t('common.delete')}
cancelButtonText={t('common.cancel')}
useInert={false}
>
<p>{t('workflows.deleteWorkflow2')}</p>
</ConfirmationAlertDialog>
<CopyWorkflowLinkModal
isOpen={copyWorkflowLinkModal.isOpen}
onClose={copyWorkflowLinkModal.close}

View File

@@ -0,0 +1,58 @@
import { ConfirmationAlertDialog, Text } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { toast } from 'features/toast/toast';
import { atom } from 'nanostores';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useDeleteWorkflowMutation, workflowsApi } from 'services/api/endpoints/workflows';
import type { WorkflowRecordListItemDTO } from 'services/api/types';
const $workflowToDelete = atom<WorkflowRecordListItemDTO | null>(null);
const clearWorkflowToDelete = () => $workflowToDelete.set(null);
export const useDeleteWorkflow = () => {
const deleteWorkflow = useCallback((workflow: WorkflowRecordListItemDTO) => {
$workflowToDelete.set(workflow);
}, []);
return deleteWorkflow;
};
export const DeleteWorkflowDialog = () => {
const { t } = useTranslation();
const workflowToDelete = useStore($workflowToDelete);
const [_deleteWorkflow] = useDeleteWorkflowMutation();
const deleteWorkflow = useCallback(async () => {
if (!workflowToDelete) {
return;
}
try {
await _deleteWorkflow(workflowToDelete.workflow_id).unwrap();
toast({
id: 'WORKFLOW_DELETED',
title: t('toast.workflowDeleted'),
});
} catch {
toast({
id: `AUTH_ERROR_TOAST_${workflowsApi.endpoints.deleteWorkflow.name}`,
title: t('toast.problemDeletingWorkflow'),
status: 'error',
});
}
}, [_deleteWorkflow, t, workflowToDelete]);
return (
<ConfirmationAlertDialog
isOpen={workflowToDelete !== null}
onClose={clearWorkflowToDelete}
title={t('workflows.deleteWorkflow')}
acceptCallback={deleteWorkflow}
acceptButtonText={t('common.delete')}
cancelButtonText={t('common.cancel')}
useInert={false}
>
<Text>{t('workflows.deleteWorkflow2')}</Text>
</ConfirmationAlertDialog>
);
};

View File

@@ -1,44 +0,0 @@
import { toast } from 'features/toast/toast';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useDeleteWorkflowMutation, workflowsApi } from 'services/api/endpoints/workflows';
type UseDeleteLibraryWorkflowOptions = {
onSuccess?: () => void;
onError?: () => void;
};
type UseDeleteLibraryWorkflowReturn = {
deleteWorkflow: (workflow_id: string) => Promise<void>;
deleteWorkflowResult: ReturnType<typeof useDeleteWorkflowMutation>[1];
};
type UseDeleteLibraryWorkflow = (arg: UseDeleteLibraryWorkflowOptions) => UseDeleteLibraryWorkflowReturn;
export const useDeleteLibraryWorkflow: UseDeleteLibraryWorkflow = ({ onSuccess, onError }) => {
const { t } = useTranslation();
const [_deleteWorkflow, deleteWorkflowResult] = useDeleteWorkflowMutation();
const deleteWorkflow = useCallback(
async (workflow_id: string) => {
try {
await _deleteWorkflow(workflow_id).unwrap();
toast({
id: 'WORKFLOW_DELETED',
title: t('toast.workflowDeleted'),
});
onSuccess && onSuccess();
} catch {
toast({
id: `AUTH_ERROR_TOAST_${workflowsApi.endpoints.deleteWorkflow.name}`,
title: t('toast.problemDeletingWorkflow'),
status: 'error',
});
onError && onError();
}
},
[_deleteWorkflow, t, onSuccess, onError]
);
return { deleteWorkflow, deleteWorkflowResult };
};