Skip to content

temporary history entries #21

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

Closed
matt-buland-sfdc opened this issue Feb 4, 2021 · 9 comments
Closed

temporary history entries #21

matt-buland-sfdc opened this issue Feb 4, 2021 · 9 comments

Comments

@matt-buland-sfdc
Copy link

matt-buland-sfdc commented Feb 4, 2021

Something I've struggled with is history maintenance for pages that temporarily have a history entry, and I'm curious on your thoughts, and how to bridge this experience with the history API (though ultimately, this may just be a user-space problem).

Consider a CRUD app:
I start at /tasks, I click a link that pushes an entry to /tasks/new, where I fill out a form (note: this could be implemented as a modal or a page, though the problem statement remains the same). We want this form to be temporary, in that when I navigate away from the form (either cancel or complete) it is no longer in the history stack, because it is not something that we want users to "return" to, but rather they start fresh each time they want to go to the page.

The main user-flow we want to solve is such that from /tasks -> /tasks/0 -> /tasks/0/edit -> complete the form -> /task/0 -> back(), the user is taken to "/tasks", instead of /tasks/0/edit. The best name I have for this type of page so far is a "temporary page". It gets cleaned up when you navigate away, specifically when you push navigate to a different page (cleanup on back from /tasks/0/edit to /tasks/0 isn't an issue).

The main question is how we should implement the success case:

  1. appHistory.pushNewEntry(url: '/tasks/0') -- this visually appears to work, but now the back-button is unintuitive: back -> /tasks/0/edit -> /tasks/0 (again)
  2. appHistory.back() -- this only works if the intended destination is the previous page (not an appropriate assumption)
  3. appHistory.back() && appHistory.updateCurrentEntry(); -- this fixes the history, but causes many navigation events, which may start rendering the previous page, only to immediately tear it down, leading to rendering inefficiencies, and view flickering in the worst case (renderer dependent). Our app also has side-effects to push vs pop navigation which makes this a bigger struggle: historical navigation allows the page to use cached information or content that is stale in the DOM, but push navigation tries to fetch the latest, and will generally freshly render the page. So really, the ideal would be to use back(); back(); pushNewEntry(); so that it's clear it should be treated like a push, but the history gets cleaned up.
  4. proposed new API: appHistory.pushEntryFrom(keyForTaskList, {url: '/tasks/0'}) -- the goals of this API would be: limit the events to only 1 navigate/currententrychange event, the history entries after key are discarded, and the navigate/change is indistinguishable from a pushNavigation (including the lack of common back/historical indicators like popstate).
  5. proposed new API: appHistory.remove(currententry) && appHistory.updateCurrentEntry({url: '/tasks/0'}) -- Inspired by Updating, deleting, and rearranging non-current entries #9 , a mutation API would allow us to remove the entry we don't want to be there, and push/replace the entry that we do, except that we're currently on the entry we want to remove (bummer). A stipulation I'd add is that I would not expect appHistory.remove to generate any events (that are currently listed in the spec), unless it is the currentEntry that is changing (which has a similar issue to approach 3). A remove/mutation API may be more extensible to other use-cases as well, as discussed in Updating, deleting, and rearranging non-current entries #9 .

I realize this may be an application specific problem that could be solved with our own navigation API that intercepts and conditionally ignores some of the events in this scenario, but it also feels like a general problem that applications may be leaving unsolved at the expense of either unnecessary rendering work or mangled browser history. Any thoughts or suggestions?

@Yay295
Copy link

Yay295 commented Feb 4, 2021

The main user-flow we want to solve is such that from /tasks -> /tasks/0 -> /tasks/0/edit -> complete the form -> /task/0 -> back(), the user is taken to /tasks, instead of /tasks/0/edit.

Do your users want this flow? This does not seem very intuitive to me. If I've just submitted an edit and decide to click the back button in my browser I would expect to go back to the edit page. If you want to have a button in your app that takes you back to the task list, why not just have a button in your app that takes you back to the task list?

@matt-buland-sfdc
Copy link
Author

Yes, users want this. Their flows are centered around dealing with many tasks, and jumping into each to do a quick edit, then leaving. In a previous revision, edits were inline, and were never even part of the browser history, so they don't want it to act like it's a page with history. We want to add edits to the history stack so that it can be preserved on page refresh, but that introduces this problem.

The issue with "a button that takes you back to the task list" is that 1. the button in this flow is the save/form button during edit, so it's not as simple as a breadcrumb button would be 2. the term "back". Our app makes some performance optimizations depending on the direction you're moving through history, so a true back would use stale content, where forward navigation reaches out for more fresh data. This makes back-navigation super snappy, but forward navigation accurate (our users navigate through tons of pages really quickly). This scenario removes history entries, but needs to appear as a forward operation to match the intent behind the navigation (to get fresh data). I'm not opposed to hacking the intent part to mask the history traversal like a forward intended navigation, but I still see it as a hack.

@domenic
Copy link
Collaborator

domenic commented Feb 4, 2021

It would probably be better then to emulate the previous flow, and just not push a history entry or change the URL when the users are editing the task?

@matt-buland-sfdc
Copy link
Author

Yeah, perhaps. I was just hoping to be able to find a way to achieve our ideal experience.

@tbondwilkinson
Copy link
Contributor

I think that this is something that a delete operation could help with, as per #9. The tricky part is still what happens if you navigate away from a temporary Entry, you would have to delete the current entry as part of your navigation.

Having some way to mark an Entry as navigation temporary would enable the browser to do all that work for you. This is something that we're doing today manually by marking entries as "deleted" and then skipping over them when the browser notifies us that we're there - truly a gross hack.

@Yay295
Copy link

Yay295 commented Feb 11, 2021

You might want the temporary entry to stay around though, temporarily, so that you can reload it if the page is refreshed, or the user decides to go back and then forward.

@jakearchibald
Copy link

jakearchibald commented Jul 6, 2021

Here's a thread about what Twitter does for modals like "retweet" https://twitter.com/samthor/status/1412331912048254979

It's basically this (and all navigations are pushState):

  1. User is on home page. History is [*home page]
  2. User navigates to tweet. History is [home page, *tweet]
  3. User navigates to retweet dialog. History is [home page, tweet, *retweet dialog]
  4. User presses back. History is [home page, *tweet, retweet dialog]
  5. App automatically goes back again, but doesn't update page. History is [*home page, tweet, retweet dialog]
  6. App pushes state for the tweet. It's a new history entry, but it's identical to the one that was already there, so it clears forward state. History is [home page, *tweet].

This doesn't always work, since there needs to be an extra "before" entry – "home page" in this example.

The ability to delete history entries would remove the need for this hack.

@jakearchibald
Copy link

It's less clear to me if opening the retweet dialog should clear forward history.

@domenic
Copy link
Collaborator

domenic commented Nov 4, 2021

Let's roll this into #9, as I think the discussion of use cases there is very similar and the solution space will be as well.

@domenic domenic closed this as completed Nov 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants