mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-14 19:44:57 -05:00
feat(ui): add conditionally-enabled workflow publishing ui
This is a squash of a lot of scattered commits that became very difficult to clean up and make individually. Sorry. Besides the new UI, there are a number of notable changes: - Publishing logic is disabled in OSS by default. To enable it, provided a `disabledFeatures` prop _without_ "publishWorkflow". - Enqueuing a workflow is no longer handled in a redux listener. It was hard to track the state of the enqueue logic in the listener. It is now in a hook. I did not migrate the canvas and upscaling tabs - their enqueue logic is still in the listener. - When queueing a validation run, the new `useEnqueueWorkflows()` hook will update the payload with the required data for the run. - Some logic is added to the socket event listeners to handle workflow publish runs completing. - The workflow library side nav has a new "published" view. It is hidden when the "publishWorkflow" feature is disabled. - I've added `Safe` and `OrThrow` versions of some workflows hooks. These hooks typically retrieve some data from redux. For example, a node. The `Safe` hooks return the node or null if it cannot be found, while the `OrThrow` hooks return the node or raise if it cannot be found. The `OrThrow` hooks should be used within one of the gate components. These components use the `Safe` hooks and render a fallback if e.g. the node isn't found. This change is required for some of the publish flow UI. - Add support for locking the workflow editor. When locked, you can pan and zoom but that's it. Currently, it is only locked during publish flow and if a published workflow is opened.
This commit is contained in:
@@ -13,28 +13,28 @@ import { atom } from 'nanostores';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Callbacks = {
|
||||
type LoadWorkflowOptions = {
|
||||
onSuccess?: (workflow: WorkflowV3) => void;
|
||||
onError?: () => void;
|
||||
onCompleted?: () => void;
|
||||
};
|
||||
|
||||
type LoadLibraryWorkflowData = Callbacks & {
|
||||
type LoadLibraryWorkflowData = LoadWorkflowOptions & {
|
||||
type: 'library';
|
||||
data: string;
|
||||
};
|
||||
|
||||
type LoadWorkflowFromObjectData = Callbacks & {
|
||||
type LoadWorkflowFromObjectData = LoadWorkflowOptions & {
|
||||
type: 'object';
|
||||
data: unknown;
|
||||
};
|
||||
|
||||
type LoadWorkflowFromFileData = Callbacks & {
|
||||
type LoadWorkflowFromFileData = LoadWorkflowOptions & {
|
||||
type: 'file';
|
||||
data: File;
|
||||
};
|
||||
|
||||
type LoadWorkflowFromImageData = Callbacks & {
|
||||
type LoadWorkflowFromImageData = LoadWorkflowOptions & {
|
||||
type: 'image';
|
||||
data: string;
|
||||
};
|
||||
|
||||
@@ -115,6 +115,7 @@ const Content = memo(({ workflow, cancelRef }: { workflow: WorkflowV3; cancelRef
|
||||
workflow.id = undefined;
|
||||
workflow.name = name;
|
||||
workflow.meta.category = shouldSaveToProject ? 'project' : 'user';
|
||||
workflow.is_published = false;
|
||||
|
||||
// We've just made the workflow a draft, but TS doesn't know that. We need to assert it.
|
||||
assert(isDraftWorkflow(workflow));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectWorkflowIsTouched } from 'features/nodes/store/workflowSlice';
|
||||
import { selectWorkflowIsPublished, selectWorkflowIsTouched } from 'features/nodes/store/workflowSlice';
|
||||
import { useSaveOrSaveAsWorkflow } from 'features/workflowLibrary/hooks/useSaveOrSaveAsWorkflow';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -10,9 +10,15 @@ const SaveWorkflowMenuItem = () => {
|
||||
const { t } = useTranslation();
|
||||
const saveOrSaveAsWorkflow = useSaveOrSaveAsWorkflow();
|
||||
const isTouched = useAppSelector(selectWorkflowIsTouched);
|
||||
const isPublished = useAppSelector(selectWorkflowIsPublished);
|
||||
|
||||
return (
|
||||
<MenuItem as="button" isDisabled={!isTouched} icon={<PiFloppyDiskBold />} onClick={saveOrSaveAsWorkflow}>
|
||||
<MenuItem
|
||||
as="button"
|
||||
isDisabled={!isTouched || !!isPublished}
|
||||
icon={<PiFloppyDiskBold />}
|
||||
onClick={saveOrSaveAsWorkflow}
|
||||
>
|
||||
{t('workflows.saveWorkflow')}
|
||||
</MenuItem>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
formFieldInitialValuesChanged,
|
||||
workflowCategoryChanged,
|
||||
workflowIDChanged,
|
||||
workflowIsPublishedChanged,
|
||||
workflowNameChanged,
|
||||
workflowSaved,
|
||||
} from 'features/nodes/store/workflowSlice';
|
||||
@@ -65,6 +66,7 @@ export const useCreateLibraryWorkflow = (): CreateLibraryWorkflowReturn => {
|
||||
meta: { category },
|
||||
} = data.workflow;
|
||||
dispatch(workflowIDChanged(id));
|
||||
dispatch(workflowIsPublishedChanged(false));
|
||||
dispatch(workflowNameChanged(name));
|
||||
dispatch(workflowCategoryChanged(category));
|
||||
dispatch(newWorkflowSaved({ category }));
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectWorkflowIsPublished } from 'features/nodes/store/workflowSlice';
|
||||
import { useBuildWorkflowFast } from 'features/nodes/util/workflow/buildWorkflow';
|
||||
import { saveWorkflowAs } from 'features/workflowLibrary/components/SaveWorkflowAsDialog';
|
||||
import { isLibraryWorkflow, useSaveLibraryWorkflow } from 'features/workflowLibrary/hooks/useSaveLibraryWorkflow';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
/**
|
||||
* Returns a function that saves the current workflow if it's a library workflow, or opens the save dialog.
|
||||
* Returns a function that saves the current workflow if it's a library workflow, or opens the save as dialog.
|
||||
*
|
||||
* Published workflows are always saved as a new workflow.
|
||||
*/
|
||||
export const useSaveOrSaveAsWorkflow = () => {
|
||||
const buildWorkflow = useBuildWorkflowFast();
|
||||
const isPublished = useAppSelector(selectWorkflowIsPublished);
|
||||
const { saveWorkflow } = useSaveLibraryWorkflow();
|
||||
|
||||
const saveOrSaveAsWorkflow = useCallback(() => {
|
||||
const workflow = buildWorkflow();
|
||||
|
||||
if (isLibraryWorkflow(workflow)) {
|
||||
if (isLibraryWorkflow(workflow) && !isPublished) {
|
||||
saveWorkflow(workflow);
|
||||
} else {
|
||||
saveWorkflowAs(workflow);
|
||||
}
|
||||
}, [buildWorkflow, saveWorkflow]);
|
||||
}, [buildWorkflow, isPublished, saveWorkflow]);
|
||||
|
||||
return saveOrSaveAsWorkflow;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user