Skip to content
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

Support back button blocking #14

Open
hoangvuanduin opened this issue May 23, 2023 · 4 comments
Open

Support back button blocking #14

hoangvuanduin opened this issue May 23, 2023 · 4 comments
Labels
enhancement New feature or request

Comments

@hoangvuanduin
Copy link

Hi, I am using Waypoint, and my use case requires the browser shows up a warning message when the user navigates out of page. Normally, I would do it like this:

dom.windowEvents.onBeforeUnload.map(_.returnValue = "") --> Observer.empty

However, it does not work when I go back after calling Waypoint router pushState. This is because Waypoint does not unload dom elements, so onBeforeUnload does not trigger. I attempted to tweak popStateEvent, however, it was kinda tricky because when onPopState triggers, the browser history has already changed, so I have to manually undo the pop state when the user decides not to leave page.

Could you please consider some support for this use case?

@raquo
Copy link
Owner

raquo commented May 23, 2023

Hm, yes, I don't think there's a good way to do this now. I wonder what kind of APIs do other routing libs have for this.

As a workaround, you could try to hack into router.currentPageSignal. For example, if you currently have something like:

div(
  child <-- SplitRender(router.currentPageSignal)....
)

You could try replacing router.currentPageSignal with something like:

{
  var maybeLastPage: Option[Page] = None
  router.currentPageSignal.map { nextPage => 
    val actualNextPage = if (okToExit) nextPage else {
      val prevPage = maybeLastPage.get
      js.setTimeout(0){ router.pushState(prevPage) } // pretend that the user didn't press the back button - keeps the router's internal state consistent with this hack.
      prevPage
    }
    maybeLastPage = Some(nextPage)
    actualNextPage
  }
}

Basically, we can't prevent the popState event, so we will prevent the propagation of that event into the rendering stage, sort of. This is incredibly hacky so I don't know how well it will work (e.g. you might want to filter out redundant events), but I don't think there is another way. onBeforeOnload will not be called unless you do a full page refresh, and the whole point of frontend routers like Waypoint is to avoid such refreshes.

@raquo raquo added the enhancement New feature or request label May 23, 2023
@hoangvuanduin
Copy link
Author

Thanks for your suggestion, I can see that there is no way to prevent the popState event. Last time I tried something similar to this, but the problem with router.pushState is it will mess up the browser history when the user hits forward instead of back (popState fires for forward as well). I think if we can't prevent the popState event no matter what, can you support a function to undo the popState event when we call it manually? I mean something to replace router.pushState in your above code but still keep the browser history instead of pushing another state.

@raquo
Copy link
Owner

raquo commented May 23, 2023

Indeed, this kind of hack can break the forward button, especially in more complex navigation sequences.

To fix this properly, I think we'd need to implement something like this. I haven't looked at it in detail yet. I think it's something along the lines of undoing the undesirable navigation post-factum. The trick is, to undo it, you need to know whether to issue a history.back() or history.forward() command, and for that, you need to know whether the user pressed the back button (in which case you need to call history.forward to undo it) or did some other kind of navigation (in which case you need to call history.back to undo it), and for that you need to add sequential indexes to page state sent to the history API. And, all of that would need to happen in the router, prior to these shenanigans being exposed to the rendering logic.

Ok I know the above is a rather incomprehensible word salad, it's mostly a hint for myself about where to pick up when I eventually get to this.

@hoangvuanduin
Copy link
Author

Thanks! I am awaiting for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants