feat(ui): standardize and clean up workflow loading hooks and logic

This commit is contained in:
psychedelicious
2025-03-12 18:23:36 +10:00
parent aed446f013
commit a29fb18c0b
13 changed files with 204 additions and 197 deletions

View File

@@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
import { selectWorkflowIsTouched, workflowModeChanged } from 'features/nodes/store/workflowSlice';
import { useGetAndLoadLibraryWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow';
import { useLoadWorkflowFromLibrary } from 'features/workflowLibrary/hooks/useLoadWorkflowFromLibrary';
import { atom } from 'nanostores';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -15,7 +15,7 @@ const cleanup = () => $workflowToLoad.set(null);
export const useLoadWorkflow = () => {
const dispatch = useAppDispatch();
const workflowLibraryModal = useWorkflowLibraryModal();
const { getAndLoadWorkflow } = useGetAndLoadLibraryWorkflow();
const loadWorkflowFromLibrary = useLoadWorkflowFromLibrary();
const isTouched = useAppSelector(selectWorkflowIsTouched);
@@ -25,11 +25,14 @@ export const useLoadWorkflow = () => {
return;
}
const { workflowId, mode } = workflow;
await getAndLoadWorkflow(workflowId);
dispatch(workflowModeChanged(mode));
await loadWorkflowFromLibrary(workflowId, {
onSuccess: () => {
dispatch(workflowModeChanged(mode));
},
});
cleanup();
workflowLibraryModal.close();
}, [dispatch, getAndLoadWorkflow, workflowLibraryModal]);
}, [dispatch, loadWorkflowFromLibrary, workflowLibraryModal]);
const loadWithDialog = useCallback(
(workflowId: string, mode: 'view' | 'edit') => {

View File

@@ -15,7 +15,7 @@ import {
} from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { graphToWorkflow } from 'features/nodes/util/workflow/graphToWorkflow';
import { useLoadWorkflow } from 'features/workflowLibrary/hooks/useLoadWorkflow';
import { useValidateAndLoadWorkflow } from 'features/workflowLibrary/hooks/useValidateAndLoadWorkflow';
import { atom } from 'nanostores';
import type { ChangeEvent } from 'react';
import { useCallback, useState } from 'react';
@@ -37,16 +37,16 @@ export const useLoadWorkflowFromGraphModal = () => {
export const LoadWorkflowFromGraphModal = () => {
const { t } = useTranslation();
const _loadWorkflow = useLoadWorkflow();
const validateAndLoadWorkflow = useValidateAndLoadWorkflow();
const { isOpen, onClose } = useLoadWorkflowFromGraphModal();
const [graphRaw, setGraphRaw] = useState<string>('');
const [workflowRaw, setWorkflowRaw] = useState<string>('');
const [unvalidatedWorkflow, setUnvalidatedWorkflow] = useState<string>('');
const [shouldAutoLayout, setShouldAutoLayout] = useState(true);
const onChangeGraphRaw = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
setGraphRaw(e.target.value);
}, []);
const onChangeWorkflowRaw = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
setWorkflowRaw(e.target.value);
setUnvalidatedWorkflow(e.target.value);
}, []);
const onChangeShouldAutoLayout = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setShouldAutoLayout(e.target.checked);
@@ -54,12 +54,12 @@ export const LoadWorkflowFromGraphModal = () => {
const parse = useCallback(() => {
const graph = JSON.parse(graphRaw);
const workflow = graphToWorkflow(graph, shouldAutoLayout);
setWorkflowRaw(JSON.stringify(workflow, null, 2));
setUnvalidatedWorkflow(JSON.stringify(workflow, null, 2));
}, [graphRaw, shouldAutoLayout]);
const loadWorkflow = useCallback(() => {
_loadWorkflow({ workflow: workflowRaw, graph: null });
const loadWorkflow = useCallback(async () => {
await validateAndLoadWorkflow(unvalidatedWorkflow);
onClose();
}, [_loadWorkflow, onClose, workflowRaw]);
}, [validateAndLoadWorkflow, onClose, unvalidatedWorkflow]);
return (
<Modal isOpen={isOpen} onClose={onClose} isCentered useInert={false}>
<ModalOverlay />
@@ -95,7 +95,7 @@ export const LoadWorkflowFromGraphModal = () => {
<FormLabel>{t('nodes.workflow')}</FormLabel>
<Textarea
h="full"
value={workflowRaw}
value={unvalidatedWorkflow}
fontFamily="monospace"
whiteSpace="pre-wrap"
overflowWrap="normal"

View File

@@ -1,33 +1,29 @@
import { Button } from '@invoke-ai/ui-library';
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
import { saveWorkflowAs } from 'features/workflowLibrary/components/SaveWorkflowAsDialog';
import { useLoadWorkflowFromFile } from 'features/workflowLibrary/hooks/useLoadWorkflowFromFile';
import { memo, useCallback, useRef } from 'react';
import { memo, useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import { useTranslation } from 'react-i18next';
import { PiUploadSimpleBold } from 'react-icons/pi';
export const UploadWorkflowButton = memo(() => {
const { t } = useTranslation();
const resetRef = useRef<() => void>(null);
const workflowLibraryModal = useWorkflowLibraryModal();
const loadWorkflowFromFile = useLoadWorkflowFromFile({
resetRef,
onSuccess: (workflow) => {
workflowLibraryModal.close();
saveWorkflowAs(workflow);
},
});
const loadWorkflowFromFile = useLoadWorkflowFromFile();
const onDropAccepted = useCallback(
(files: File[]) => {
if (!files[0]) {
([file]: File[]) => {
if (!file) {
return;
}
loadWorkflowFromFile(files[0]);
loadWorkflowFromFile(file, {
onSuccess: () => {
workflowLibraryModal.close();
},
});
},
[loadWorkflowFromFile]
[loadWorkflowFromFile, workflowLibraryModal]
);
const { getInputProps, getRootProps } = useDropzone({
@@ -36,6 +32,7 @@ export const UploadWorkflowButton = memo(() => {
noDrag: true,
multiple: false,
});
return (
<>
<Button

View File

@@ -1,32 +1,29 @@
import { MenuItem } from '@invoke-ai/ui-library';
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal';
import { saveWorkflowAs } from 'features/workflowLibrary/components/SaveWorkflowAsDialog';
import { useLoadWorkflowFromFile } from 'features/workflowLibrary/hooks/useLoadWorkflowFromFile';
import { memo, useCallback, useRef } from 'react';
import { memo, useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import { useTranslation } from 'react-i18next';
import { PiUploadSimpleBold } from 'react-icons/pi';
const UploadWorkflowMenuItem = () => {
const { t } = useTranslation();
const resetRef = useRef<() => void>(null);
const workflowLibraryModal = useWorkflowLibraryModal();
const loadWorkflowFromFile = useLoadWorkflowFromFile({
resetRef,
onSuccess: (workflow) => {
workflowLibraryModal.close();
saveWorkflowAs(workflow);
},
});
const loadWorkflowFromFile = useLoadWorkflowFromFile();
const onDropAccepted = useCallback(
(files: File[]) => {
if (!files[0]) {
([file]: File[]) => {
if (!file) {
return;
}
loadWorkflowFromFile(files[0]);
loadWorkflowFromFile(file, {
onSuccess: () => {
workflowLibraryModal.close();
},
});
},
[loadWorkflowFromFile]
[loadWorkflowFromFile, workflowLibraryModal]
);
const { getRootProps, getInputProps } = useDropzone({
@@ -35,6 +32,7 @@ const UploadWorkflowMenuItem = () => {
noDrag: true,
multiple: false,
});
return (
<MenuItem as="button" icon={<PiUploadSimpleBold />} {...getRootProps()}>
{t('workflows.uploadWorkflow')}