I think I’ve actually found an okay solution for it now. Would be cool to hear your thoughts.
Instead of only the “root” state having a history, I create each new Editor View instance with their own history as well - contrary to the Split View example. But, the views are not handling their own undo/redo mechanisms, the root state does.
I am still updating the root state, when the editors dispatch state updates. State Effects the root state gets updated with only include the ID of a specific segment/View. This way, the root state essentially keeps history of the order in which changes to the different views were made and does not care what the changes were.
Basically, I have three State Effects for handling this; one for basic dispatch, one for undo and one for redo.
When a View dispatches, root state gets updated with the “basic effect” that includes an ID for retrieving this specific View in my State Field. Root state’s “invertedEffects” is configured to apply the “undo effect” for this one. Undo functionality only gets triggered on the root state and when it does, the main State Field handles the “undo effect” by calling the “undo” function on the specific View with the ID coming from this State Effect.
The resulting dispatched change from the View will now include “undo” user event. This will update the root state with the “undo effect”, which inverted effect is the “redo effect”. So, when root state receives a “redo” trigger, it knows that it has to now instead call “redo” for the View with this specific ID. Obviously, the function handling the dispatch also adds an annotation, that prevents my State Field from running undo/redo in an infinite loop.
Function handling dispatch also checks redo and undo depths of transaction’s start state and the resulting state. It will not update the root state if the dispatched transaction does not affect the history depth. This way, I only update the root when reversible changes are being made, since that’s what makes sense to keep track of.
Thoughts? It works pretty well now and does exactly what I need it to do. Are there some potential pitfalls I should think about perhaps?