Account for route changes in undo/redo stack
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3546285. -->
Reported by: [balintbrews](https://www.drupal.org/user/613760)
Related to !141 !65
>>>
<h3 id="overview">Overview</h3>
<p>Account for navigation and route changes when handling the undo/redo stack and performing undo/redo. Make sure it's always clear to users what is being undone/redone. This will enable us to expand the undo history to the code editor (<span class="drupalorg-gitlab-issue-link project-issue-status-info project-issue-status-4"><a href="https://www.drupal.org/project/canvas/issues/3547106" title="Status: Postponed">#3547106: Include code editor in undo/redo</a></span>).</p>
<h3 id="proposed-resolution">Proposed resolution</h3>
<p>Currently undo/redo covers two RTK slices: <code>layoutModel</code> and <code>pageData</code>. They are both wrapped with the <code>undoable()</code> reducer enhancer by <code>redux-undo</code>, which means they both track their own history. To present a single, linear timeline to the user, <code>uiSlice</code> maintains an <code>undoStack</code> to track which which slice's state needs to be changed.</p>
<h4>[⛔ Abandoned] Approach #0: Multiple, contextualized undo stacks</h4>
<p>Before creating this issue, I worked on a concept to handle multiple undo stacks, each of them associated with one or more slices. The idea was that you would always undo what you can see based on what undo stack is activated. Thinking about <span class="drupalorg-gitlab-issue-link project-issue-status-info project-issue-status-4"><a href="https://www.drupal.org/project/canvas/issues/3547106" title="Status: Postponed">#3547106: Include code editor in undo/redo</a></span>, my goal was to allow creating an undo stack for each code component, and even separating JS, CSS, and global CSS changes, so we mimic the behavior of an IDE, where you always undo changes in the currently open file.</p>
<p>Where this breaks down is the case where a slice would belong to multiple undo stacks. For example, while editing the JS source of a code component, props can also be edited. Switching to editing the CSS source leaves editing the props on the UI. While we could technically coordinate a timeline, in practice the experience becomes unintuitive and confusing.</p>
<h4>[⛔ Abandoned] Approach #1: Navigation as an undoable slice</h4>
<p>→ <a href="https://git.drupalcode.org/project/canvas/-/merge_requests/65">Merge request !65</a></p>
<p>Introduce a new <code>navigationSlice</code> to track the current route, and enhance it with <code>redux-undo</code> to build a history. Synchronize route changes between the store and the actual routing. Cover <code>navigationSlice</code> in the <code>uiSlice</code>'s <code>undoStack</code>.</p>
<p>This was fully implemented in <a href="https://git.drupalcode.org/project/canvas/-/merge_requests/65">MR !65.</a>, along with reducers to set (and navigate to) the current route without an entry in the undo history, or to perform a chained undo for actions where navigation happens alongside the operations. (For example, adding a component instance to the editor frame also navigates to the route where it is selected.)</p>
<p>A significant problem was identified with this approach by @jessebaker. In an undo/redo stack whenever you go to a past state, and make a change there, you lose the ability to redo. This is the expected behavior. However, we make it extremely easy and unintuitive to lose the redo stack by including navigation. Users can go back in history, and simply clicking on another component in the editor frame, which triggers a route change, would make the redo stack gone.</p>
<h4>[✅] Approach #2: Route information in undo stack</h4>
<p>→ <a href="https://git.drupalcode.org/project/canvas/-/merge_requests/141">Merge request !141</a></p>
<p>Idea from @jessebaker.<br>
Save a snapshot of the current route with each entry in the undo/redo stack. Navigate to the route when undo/redo is performed.</p>
<h3 id="ui-changes">User interface changes</h3>
<p>[⛔ Abandoned] Approach #0: Only what is currently visible on the UI can be undone/redone, and navigating around the UI changes the undo stack context. This mimics the behavior of IDEs.<br>
[⛔ Abandoned] Approach #1: Navigation is an undoable action on its own. The undo/redo buttons and keyboard shortcuts can trigger navigation.<br>
[✅] Approach #2: When undo/redo is performed, the UI is automatically navigated to the route where the previous/next action occurred.</p>
> Related issue: [Issue #3547106](https://www.drupal.org/node/3547106)
> Related issue: [Issue #3508317](https://www.drupal.org/node/3508317)
issue