Here's an idea for people to think about...
Right now we have transforms, and if you want to do an async transform, we say just later on call editor.onChange(newState). This works fine for applying the transform.
But in terms of the history, this ends up with a weird result, where a new change is inserted at a potentially later time, not when the user was expecting it. Usually it's not really an issue, but if the async task takes a while to complete, there might be other transforms that have occurred since. Or if the original change was a "two part" change, now you've got this extra undo step that puts it in an indeterminate state...
For example:
file.image_block with data.file = filefile locally and set data.preview = dataUridata.url = url when the upload completesNow you've got three different undo steps. The first change was when the user actually committed the action. The second one was when there the first visual feedback for their action being completed. And the third is when the data is actually "saved/stable" as far as your data model is concerned.
You probably don't want the user to be able to undo in steps.
It would be cool if there was a way to "defer" operations in a transform. Such that you could create one initial transform, and later add operations to it, so that they are grouped into a single undo step.
Not sure what the API might look like. Thoughts welcome!
@ianstormtaylor Seems like you are suggesting two connected options - a group option (only groups and makes operations act as a group) and a defer (promisified or blocking transform operation) option? Is that correct?
I could see the group option being really useful in collaborative editing, or to give more control to the developer about how to work with undos and redos. The apply method takes a save option, and could also take a group option, which groups the operation into a single step.
For the defer option, would you want to also prevent any other transform from acting on the document or the same block until all operations are completed?
A non-blocking way for the defer I could imagine would be to set up the API would be almost as a promisified-operation that is called in the transform chain, and is somehow grouped with the rest of the chain?
I think you're right, it's more about grouping many changes into one. We have the merge: true option now, but it's not enough for complex cases.
And the deferring can just happen the current way, where brand new changes can be started at any point if you have a reference to the editor.
This would be awesome! In my domain I have a whole lot of situations where I cannot do without something like this to keep history clean and sensible.
At first I thought I'd modify the history, but couldn't determine a clear way to do that. Then I saw how "merge" is determined in "apply" method of "transform" model for couple of situations like "isOnlySelections", "isContiguousInserts", "isContiguousRemoves". What I then wanted was to have my own "merge" logic. I do not think I can do that without extending the "transform" model.
One possible approach is perhaps to expose "merge" logic out of "transform"?
A more fine grained history manipulation api beyond undo and redo would also be nice perhaps.
@yalu I'm definitely open to ideas. If you have concepts for what the API might look like, feel free to throw them here. Even if they are just random sketches. Would help to get my brain thinking.
I was just a bit excited as I have been stuck with similar problem(with the way you described) for a while. But it seems my case should work just the way it is because "merge" and "save" options are exposed already. I don't know what I was thinking. It seems what I need is "merge" to be true and "save" to be false.
I'm probably mistaken but, couldn't the problem you described be solved by explicitly manipulating "merge" and "save" options as well?
I'll play along this line and see what I can come up with.
Nevermind the question about "merge" and "save".
I just realized setting "merge" and "save" won't cut it if some other transform happens in-between.
I might be totally out of line but, if each save point has an id, async transform could refer it to save(group) itself with that save point.
I think this actually isn't going to be possible, since the order that the operations actually occurred needs to be preserved for the undo stack to make any sense鈥攐therwise I'm fairly sure it'll throw errors or end up with weird cases that don't make sense.
For the initial example I gave, the desired UX is actually possible by just only saving the first change into the history, and then the subsequent updates to the image should not be saved. That way there's only a single undo step that removes the image block itself.