mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): add dialog when loading workflow if unsaved changes
This commit is contained in:
@@ -973,6 +973,8 @@
|
||||
"newWorkflow": "New Workflow",
|
||||
"newWorkflowDesc": "Create a new workflow?",
|
||||
"newWorkflowDesc2": "Your current workflow has unsaved changes.",
|
||||
"loadWorkflowDesc": "Load workflow?",
|
||||
"loadWorkflowDesc2": "Your current workflow has unsaved changes.",
|
||||
"clearWorkflow": "Clear Workflow",
|
||||
"clearWorkflowDesc": "Clear this workflow and start a new one?",
|
||||
"clearWorkflowDesc2": "Your current workflow has unsaved changes.",
|
||||
|
||||
@@ -36,6 +36,7 @@ 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 { LoadWorkflowConfirmationAlertDialog } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog';
|
||||
import { NewWorkflowConfirmationAlertDialog } from 'features/workflowLibrary/components/NewWorkflowConfirmationAlertDialog';
|
||||
import i18n from 'i18n';
|
||||
import { size } from 'lodash-es';
|
||||
@@ -75,6 +76,7 @@ const App = ({ config = DEFAULT_CONFIG, studioInitAction }: Props) => {
|
||||
<CancelAllExceptCurrentQueueItemConfirmationAlertDialog />
|
||||
<ClearQueueConfirmationsAlertDialog />
|
||||
<NewWorkflowConfirmationAlertDialog />
|
||||
<LoadWorkflowConfirmationAlertDialog />
|
||||
<DeleteStylePresetDialog />
|
||||
<DeleteWorkflowDialog />
|
||||
<ShareWorkflowModal />
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Badge, Flex, IconButton, Spacer, Text, Tooltip } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $projectUrl } from 'app/store/nanostores/projectId';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import dateFormat, { masks } from 'dateformat';
|
||||
import { useWorkflowListMenu } from 'features/nodes/store/workflowListMenu';
|
||||
import { selectWorkflowId, workflowModeChanged } from 'features/nodes/store/workflowSlice';
|
||||
import { selectWorkflowId } from 'features/nodes/store/workflowSlice';
|
||||
import { useDeleteWorkflow } from 'features/workflowLibrary/components/DeleteLibraryWorkflowConfirmationAlertDialog';
|
||||
import { useLoadWorkflow } from 'features/workflowLibrary/components/LoadWorkflowConfirmationAlertDialog';
|
||||
import { useDownloadWorkflow } from 'features/workflowLibrary/hooks/useDownloadWorkflow';
|
||||
import { useGetAndLoadLibraryWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow';
|
||||
import type { MouseEvent } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -19,9 +18,7 @@ import { WorkflowListItemTooltip } from './WorkflowListItemTooltip';
|
||||
|
||||
export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListItemDTO }) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const projectUrl = useStore($projectUrl);
|
||||
const workflowListMenu = useWorkflowListMenu();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleMouseOver = useCallback(() => {
|
||||
@@ -36,33 +33,21 @@ export const WorkflowListItem = ({ workflow }: { workflow: WorkflowRecordListIte
|
||||
const downloadWorkflow = useDownloadWorkflow();
|
||||
const shareWorkflow = useShareWorkflow();
|
||||
const deleteWorkflow = useDeleteWorkflow();
|
||||
const { getAndLoadWorkflow } = useGetAndLoadLibraryWorkflow({
|
||||
onSuccess: workflowListMenu.close,
|
||||
});
|
||||
const loadWorkflow = useLoadWorkflow();
|
||||
|
||||
const isActive = useMemo(() => {
|
||||
return workflowId === workflow.workflow_id;
|
||||
}, [workflowId, workflow.workflow_id]);
|
||||
|
||||
const handleClickLoad = useCallback(
|
||||
(e: MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation();
|
||||
getAndLoadWorkflow(workflow.workflow_id);
|
||||
workflowListMenu.close();
|
||||
},
|
||||
[getAndLoadWorkflow, workflow.workflow_id, workflowListMenu]
|
||||
);
|
||||
const handleClickLoad = useCallback(() => {
|
||||
setIsHovered(false);
|
||||
loadWorkflow.loadWithDialog(workflow.workflow_id, 'view');
|
||||
}, [loadWorkflow, workflow.workflow_id]);
|
||||
|
||||
const handleClickEdit = useCallback(
|
||||
async (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
setIsHovered(false);
|
||||
await getAndLoadWorkflow(workflow.workflow_id);
|
||||
dispatch(workflowModeChanged('edit'));
|
||||
workflowListMenu.close();
|
||||
},
|
||||
[getAndLoadWorkflow, workflow.workflow_id, dispatch, workflowListMenu]
|
||||
);
|
||||
const handleClickEdit = useCallback(() => {
|
||||
setIsHovered(false);
|
||||
loadWorkflow.loadWithDialog(workflow.workflow_id, 'view');
|
||||
}, [loadWorkflow, workflow.workflow_id]);
|
||||
|
||||
const handleClickDelete = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ConfirmationAlertDialog, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { useWorkflowListMenu } from 'features/nodes/store/workflowListMenu';
|
||||
import { selectWorkflowIsTouched, workflowModeChanged } from 'features/nodes/store/workflowSlice';
|
||||
import { useGetAndLoadLibraryWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow';
|
||||
import { atom } from 'nanostores';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const $workflowToLoad = atom<{ workflowId: string; mode: 'view' | 'edit'; isOpen: boolean } | null>(null);
|
||||
const cleanup = () => $workflowToLoad.set(null);
|
||||
|
||||
export const useLoadWorkflow = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const workflowListMenu = useWorkflowListMenu();
|
||||
const { getAndLoadWorkflow } = useGetAndLoadLibraryWorkflow();
|
||||
|
||||
const isTouched = useAppSelector(selectWorkflowIsTouched);
|
||||
|
||||
const loadImmediate = useCallback(async () => {
|
||||
const workflow = $workflowToLoad.get();
|
||||
if (!workflow) {
|
||||
return;
|
||||
}
|
||||
const { workflowId, mode } = workflow;
|
||||
await getAndLoadWorkflow(workflowId);
|
||||
dispatch(workflowModeChanged(mode));
|
||||
cleanup();
|
||||
workflowListMenu.close();
|
||||
}, [dispatch, getAndLoadWorkflow, workflowListMenu]);
|
||||
|
||||
const loadWithDialog = useCallback(
|
||||
(workflowId: string, mode: 'view' | 'edit') => {
|
||||
if (!isTouched) {
|
||||
$workflowToLoad.set({
|
||||
workflowId,
|
||||
mode,
|
||||
isOpen: false,
|
||||
});
|
||||
loadImmediate();
|
||||
} else {
|
||||
$workflowToLoad.set({
|
||||
workflowId,
|
||||
mode,
|
||||
isOpen: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[loadImmediate, isTouched]
|
||||
);
|
||||
|
||||
return {
|
||||
loadImmediate,
|
||||
loadWithDialog,
|
||||
} as const;
|
||||
};
|
||||
|
||||
export const LoadWorkflowConfirmationAlertDialog = memo(() => {
|
||||
useAssertSingleton('LoadWorkflowConfirmationAlertDialog');
|
||||
const { t } = useTranslation();
|
||||
const workflow = useStore($workflowToLoad);
|
||||
const loadWorkflow = useLoadWorkflow();
|
||||
|
||||
return (
|
||||
<ConfirmationAlertDialog
|
||||
isOpen={!!workflow?.isOpen}
|
||||
onClose={cleanup}
|
||||
title={t('nodes.loadWorkflow')}
|
||||
acceptCallback={loadWorkflow.loadImmediate}
|
||||
useInert={false}
|
||||
>
|
||||
<Flex flexDir="column" gap={2}>
|
||||
<Text>{t('nodes.loadWorkflowDesc')}</Text>
|
||||
<Text variant="subtext">{t('nodes.loadWorkflowDesc2')}</Text>
|
||||
</Flex>
|
||||
</ConfirmationAlertDialog>
|
||||
);
|
||||
});
|
||||
|
||||
LoadWorkflowConfirmationAlertDialog.displayName = 'LoadWorkflowConfirmationAlertDialog';
|
||||
Reference in New Issue
Block a user