mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): more workflow loading standardization
There is now a single entrypoint for loading a workflow - `useLoadWorkflowWithDialog`. The hook: Handles loading workflows from various sources. If there are unsaved changes, the user will be prompted to confirm before loading the workflow. It returns a function that: Loads a workflow from various sources. If there are unsaved changes, the user will be prompted to confirm before loading the workflow. The workflow will be loaded immediately if there are no unsaved changes. On success, error or completion, the corresponding callback will be called. WHEW
This commit is contained in:
@@ -4,6 +4,12 @@ import { useValidateAndLoadWorkflow } from 'features/workflowLibrary/hooks/useVa
|
||||
import { workflowLoadedFromFile } from 'features/workflowLibrary/store/actions';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
/**
|
||||
* Loads a workflow from a file.
|
||||
*
|
||||
* You probably should instead use `useLoadWorkflowWithDialog`, which opens a dialog to prevent loss of unsaved changes
|
||||
* and handles the loading process.
|
||||
*/
|
||||
export const useLoadWorkflowFromFile = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const validatedAndLoadWorkflow = useValidateAndLoadWorkflow();
|
||||
@@ -13,30 +19,37 @@ export const useLoadWorkflowFromFile = () => {
|
||||
options: {
|
||||
onSuccess?: (workflow: WorkflowV3) => void;
|
||||
onError?: () => void;
|
||||
onCompleted?: () => void;
|
||||
} = {}
|
||||
) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
const rawJSON = reader.result;
|
||||
const { onSuccess, onError } = options;
|
||||
try {
|
||||
const unvalidatedWorkflow = JSON.parse(rawJSON as string);
|
||||
const validatedWorkflow = await validatedAndLoadWorkflow(unvalidatedWorkflow);
|
||||
return new Promise<WorkflowV3 | void>((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
const rawJSON = reader.result;
|
||||
const { onSuccess, onError, onCompleted } = options;
|
||||
try {
|
||||
const unvalidatedWorkflow = JSON.parse(rawJSON as string);
|
||||
const validatedWorkflow = await validatedAndLoadWorkflow(unvalidatedWorkflow);
|
||||
|
||||
if (!validatedWorkflow) {
|
||||
reader.abort();
|
||||
if (!validatedWorkflow) {
|
||||
reader.abort();
|
||||
onError?.();
|
||||
return;
|
||||
}
|
||||
dispatch(workflowLoadedFromFile());
|
||||
onSuccess?.(validatedWorkflow);
|
||||
resolve(validatedWorkflow);
|
||||
} catch {
|
||||
// This is catching the error from the parsing the JSON file
|
||||
onError?.();
|
||||
return;
|
||||
reject();
|
||||
} finally {
|
||||
onCompleted?.();
|
||||
}
|
||||
dispatch(workflowLoadedFromFile());
|
||||
onSuccess?.(validatedWorkflow);
|
||||
} catch {
|
||||
// This is catching the error from the parsing the JSON file
|
||||
onError?.();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
reader.readAsText(file);
|
||||
});
|
||||
},
|
||||
[validatedAndLoadWorkflow, dispatch]
|
||||
);
|
||||
|
||||
@@ -8,19 +8,26 @@ import { useLazyGetImageWorkflowQuery } from 'services/api/endpoints/images';
|
||||
import type { NonNullableGraph } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
/**
|
||||
* Loads a workflow from an image.
|
||||
*
|
||||
* You probably should instead use `useLoadWorkflowWithDialog`, which opens a dialog to prevent loss of unsaved changes
|
||||
* and handles the loading process.
|
||||
*/
|
||||
export const useLoadWorkflowFromImage = () => {
|
||||
const { t } = useTranslation();
|
||||
const [getWorkflowAndGraphFromImage, result] = useLazyGetImageWorkflowQuery();
|
||||
const [getWorkflowAndGraphFromImage] = useLazyGetImageWorkflowQuery();
|
||||
const validateAndLoadWorkflow = useValidateAndLoadWorkflow();
|
||||
const getAndLoadEmbeddedWorkflow = useCallback(
|
||||
const loadWorkflowFromImage = useCallback(
|
||||
async (
|
||||
imageName: string,
|
||||
options: {
|
||||
onSuccess?: (workflow: WorkflowV3) => void;
|
||||
onError?: () => void;
|
||||
onCompleted?: () => void;
|
||||
} = {}
|
||||
) => {
|
||||
const { onSuccess, onError } = options;
|
||||
const { onSuccess, onError, onCompleted } = options;
|
||||
try {
|
||||
const { workflow, graph } = await getWorkflowAndGraphFromImage(imageName).unwrap();
|
||||
|
||||
@@ -52,11 +59,12 @@ export const useLoadWorkflowFromImage = () => {
|
||||
status: 'error',
|
||||
});
|
||||
onError?.();
|
||||
return;
|
||||
} finally {
|
||||
onCompleted?.();
|
||||
}
|
||||
},
|
||||
[getWorkflowAndGraphFromImage, validateAndLoadWorkflow, t]
|
||||
);
|
||||
|
||||
return [getAndLoadEmbeddedWorkflow, result] as const;
|
||||
return loadWorkflowFromImage;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,12 @@ import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLazyGetWorkflowQuery, useUpdateOpenedAtMutation, workflowsApi } from 'services/api/endpoints/workflows';
|
||||
|
||||
/**
|
||||
* Loads a workflow from the library.
|
||||
*
|
||||
* You probably should instead use `useLoadWorkflowWithDialog`, which opens a dialog to prevent loss of unsaved changes
|
||||
* and handles the loading process.
|
||||
*/
|
||||
export const useLoadWorkflowFromLibrary = () => {
|
||||
const toast = useToast();
|
||||
const { t } = useTranslation();
|
||||
@@ -17,9 +23,10 @@ export const useLoadWorkflowFromLibrary = () => {
|
||||
options: {
|
||||
onSuccess?: (workflow: WorkflowV3) => void;
|
||||
onError?: () => void;
|
||||
onCompleted?: () => void;
|
||||
} = {}
|
||||
) => {
|
||||
const { onSuccess, onError } = options;
|
||||
const { onSuccess, onError, onCompleted } = options;
|
||||
try {
|
||||
const res = await getWorkflow(workflowId).unwrap();
|
||||
|
||||
@@ -39,6 +46,8 @@ export const useLoadWorkflowFromLibrary = () => {
|
||||
status: 'error',
|
||||
});
|
||||
onError?.();
|
||||
} finally {
|
||||
onCompleted?.();
|
||||
}
|
||||
},
|
||||
[getWorkflow, validateAndLoadWorkflow, updateOpenedAt, toast, t]
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { WorkflowV3 } from 'features/nodes/types/workflow';
|
||||
import { useValidateAndLoadWorkflow } from 'features/workflowLibrary/hooks/useValidateAndLoadWorkflow';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
/**
|
||||
* Loads a workflow from an object.
|
||||
*
|
||||
* You probably should instead use `useLoadWorkflowWithDialog`, which opens a dialog to prevent loss of unsaved changes
|
||||
* and handles the loading process.
|
||||
*/
|
||||
export const useLoadWorkflowFromObject = () => {
|
||||
const validateAndLoadWorkflow = useValidateAndLoadWorkflow();
|
||||
const loadWorkflowFromObject = useCallback(
|
||||
async (
|
||||
unvalidatedWorkflow: unknown,
|
||||
options: {
|
||||
onSuccess?: (workflow: WorkflowV3) => void;
|
||||
onError?: () => void;
|
||||
onCompleted?: () => void;
|
||||
} = {}
|
||||
) => {
|
||||
const { onSuccess, onError, onCompleted } = options;
|
||||
try {
|
||||
const validatedWorkflow = await validateAndLoadWorkflow(unvalidatedWorkflow);
|
||||
|
||||
if (!validatedWorkflow) {
|
||||
onError?.();
|
||||
return;
|
||||
}
|
||||
onSuccess?.(validatedWorkflow);
|
||||
} finally {
|
||||
onCompleted?.();
|
||||
}
|
||||
},
|
||||
[validateAndLoadWorkflow]
|
||||
);
|
||||
|
||||
return loadWorkflowFromObject;
|
||||
};
|
||||
@@ -17,6 +17,21 @@ import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
const log = logger('workflows');
|
||||
|
||||
/**
|
||||
* This hook manages the lower-level workflow validation and loading process.
|
||||
*
|
||||
* You probably should instead use `useLoadWorkflowWithDialog`, which opens a dialog to prevent loss of unsaved changes
|
||||
* and handles the loading process.
|
||||
*
|
||||
* Internally, `useLoadWorkflowWithDialog` uses these hooks...
|
||||
*
|
||||
* - `useLoadWorkflowFromFile`
|
||||
* - `useLoadWorkflowFromImage`
|
||||
* - `useLoadWorkflowFromLibrary`
|
||||
* - `useLoadWorkflowFromObject`
|
||||
*
|
||||
* ...each of which internally uses hook.
|
||||
*/
|
||||
export const useValidateAndLoadWorkflow = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
Reference in New Issue
Block a user