Whats an example of the minimal code required to undo a transformation?

I’m trying to temporarily prevent a transaction from occurring until a user confirms an action.

So I’d like to undo a user’s edits, save it, and apply it once they confirm the action.

I tried looking at history plugin and the tracking changes example, but there’s a lot of code involved so it’s hard to know what’s absolutely necessary.

Wondering if anyone more experienced would be kind enough to post a minimal example?

(Upon writing this I’ve thought of potential ways that I could do the above without touching steps, mappings etc. but I figure this is a good chance to get to know Prosemirror better, so any comments are appreciated!)

Hmmm you’d want to use the undo and redo commands from the History plugin. I recommend against this though. You should try and figure out a way to set this up outside of ProseMirror, so that upon confirm, your code creates and dispatches the transformation/transaction. One of the issues you’ll run into doing it your way is if you ever want to setup collaborative editing, those changes will get pushed and may create visual noise for your users!

What are you trying to have the user confirm?

This is probably another use case for amending a transaction see this recent discussion.

At the moment I’m just hijacking filterTransaction - you can either mutate the tr coming in here (although that’s not really supported) to reset it and pass the details form the un-reset tr through setMeta to your plugin state. Or you can actually filter the transaction and store the most recent transaction in your closure somewhere. Neither of these seem that nice, but they work for the most part …

To undo a transaction, create a new transaction with inverted versions for each of its steps

  let inverted = state.transaction
  for (let i = tr.steps.length - 1; i >= 0; i--)

Just ‘storing’ those somewhere and applying the later isn’t trivial, though—the document may have changed in the meantime, at which point you’ll have to ‘rebase’ the steps over any intermediate changes. I can’t show that in a quick example, but you can study the collab module to learn more about it.

1 Like

This is exactly the example I was looking for as a foundation. Thank you.

To balance things out - how would a minimal example of redoing look?

I still find this causes issues with undo / redo behaviour when appending this. Although this could be specific to something I’m misunderstanding with the undo API.

My concern was that the undo / redo command 1) might show the action happening / unhappening, or create conflicts with history 2) wasn’t sure how to trigger them programmatically.

I’m trying to make it so that the user has to confirm the deletion of important nodes.

I think setting it up outside the editor might be bes idea. I was setting it up inside since since I was doing it via a plugin and needed the modal to get triggered by the plugin’s view. (So I couldn’t just filter the transaction, etc.)

The problem is that filterTransaction prevents the tr from propagating all together. So I was leaning towards appendTransaction (and undoing it) since I wanted it all to happen inside a plugin. it seemed like a lot of work, but I wanted to learn how I’d go about doing it anyway.

As suggested above, handling it outside of prosemirror might be the simplest option. I also commented in thread you linked about how I’ve replaced transactions that way before.

1 Like

The same thing, but with the inverted changes as input.

Right, the history will treat these as regular changes, and add them to the undo stack. Depending on what the goal is here, but if you can, preventing the changes from happening in the first place is usually preferable to rolling them back (and forth again).

That sounds like adding logic to dispatchTransaction to intercept such transactions before they are applied is a better approach.

But in general, a good pattern for dealing with ‘dangerous’ user actions is… the undo history. Maybe just show a tooltip with “you just deleted so-and-so, undo?” after they do such a thing?

Depending on the node type you can maybe also make the nodes isolating and then have commands (perhaps as buttons inside a decoration) that will allow you to handle the deletion in a different way?

Some blocks are associated with external data. I.e. the user can create “flashcards” inside their notes. (I’m actually super excited about this feature, can’t wait to show it to you guys.)

Which is why I have to prevent the transaction all together until it is confirmed. But the comments in this thread have give me some ideas on how to go about it, so thanks everyone