Skip to content

refactor(ui): workflow unsaved changes/published state tracking #7891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 7, 2025

Conversation

psychedelicious
Copy link
Collaborator

Summary

  • Rework unsaved changes tracking, fixing a number of issues and some awkward UX.
  • Make published state server-side state.

Related Issues / Discussions

n/a

QA Instructions

Try opening and editing a workflow. It should track unsaved changes correctly. The workflow empty state should be detected correctly (i.e. when the workflow is identical to what you get when you make a new workflow, you should see empty state prompting you to load or create a new workflow).

Merge Plan

n/a

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Tests added / updated (if applicable)
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

@github-actions github-actions bot added the frontend PRs that change frontend files label Apr 7, 2025
Previously, the workflow form's root element id was random. Every time we reset the workflow editor, the root id changed. This makes it difficult to check if the workflow editor is untouched (in its default state).

Now that root element's id is simply "root". I can't imagine any way that this would break anything.
Accidentally left in from prev change
Previously, we maintained an `isTouched` flag in redux state to indicate if a workflow had unsaved changes. We manually updated this whenever we changed something on the workflow.

This was tedious and error-prone. It also didn't handle undo/redo, so if you made a change to a node and undid it, we'd still think the workflow had unsaved changes.

Moving forward, we use a simpler and more robust strategy by hashing the server's version of the workflow and comparing it to the client's version of the workflow.

The hashing uses `stable-hash`, which is both fast and, well, stable. Most importantly, the ordering of keys in hashed objects does not change the resultant hash.

- Remove `isTouched` state entirely.
- Extract the logic that builds the "preview" workflow object from redux state into its own hook. This "preview" workflow is what we send to the server when saving a workflow. This "preview" workflow is effectively the client version of the workflow.
- Add `useDoesWorkflowHaveUnsavedChanges()` hook, which compares the hash of the client workflow and server workflow (if it exists).
- Add `useIsWorkflowUntouched()` hook, which compares the hash of the client workflow and the initial workflow that you get when you click new workflow.
- Remove `reactflow` workaround in the nodes slice undo/redo filter. When we set the nodes state while loading a workflow, `reactflow` emits a nodes size/placement change event. This triggered up our `isTouched` flag logic and marked the workflow as unsaved right from the get-go. With the new strategy to track touched status, this workaround can be removed.
- Update all logic that tracked the old `isTouched` flag to use the new hooks.
Whether a workflow is published or not shouldn't be something stored on the client. It's properly server-side state.

This change removes the `is_published` flag from redux and updates all references to the flag to use the getWorkflow query.

It also updates the socket event listener that handles session complete events. When a validation run completes, we invalidate the tags for the getWorkflow query. We need to do a bit of juggling to avoid a race condition (documented in the code). Works well though.
@psychedelicious psychedelicious force-pushed the psyche/feat/ui/workflow-touched branch from 3ff40b0 to 689bd2f Compare April 7, 2025 20:04
@psychedelicious psychedelicious requested a review from Copilot April 7, 2025 20:52
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 29 out of 29 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (3)

invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/PublishWorkflowPanelContent.tsx:251

  • [nitpick] Consider renaming the 'isWorkflowSaved' prop to a name that directly reflects its meaning (such as 'hasUnsavedChanges') to avoid ambiguity from the negation logic.
isWorkflowSaved={!doesWorkflowHaveUnsavedChanges}

invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/IsolatedWorkflowBuilderWatcher.tsx:58

  • Consider adding test cases to verify the behavior of the new unsaved changes tracking logic implemented in this hook.
export const useDoesWorkflowHaveUnsavedChanges = () => {

invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts:89

  • The removal of 'state.isTouched' updates from the action handlers requires comprehensive integration tests to ensure unsaved changes tracking still works as expected.
state.name = action.payload;

@psychedelicious psychedelicious merged commit 982603e into main Apr 7, 2025
11 checks passed
@psychedelicious psychedelicious deleted the psyche/feat/ui/workflow-touched branch April 7, 2025 20:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
frontend PRs that change frontend files
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants