backToOriginal,
}
-let paused: GhostNavigation = GhostNavigation.none
+let navigationState: GhostNavigation = GhostNavigation.none
window.addEventListener('popstate', function customPopListener(event) {
let { state } = event
+ console.log('popstate!', navigationState, event.state)
- console.log('popstate!', paused, event.state)
- if (paused !== GhostNavigation.none) {
- if (paused === GhostNavigation.restoreGhostUrl) {
+ // nested state machine to handle
+ if (navigationState !== GhostNavigation.none) {
+ if (navigationState === GhostNavigation.restoreGhostUrl) {
webHistory.replace(state.ghostURL)
console.log('replaced ghost', state.ghostURL)
- paused = GhostNavigation.backToOriginal
+ navigationState = GhostNavigation.backToOriginal
webHistory.back(false)
- } else if (paused === GhostNavigation.backToOriginal) {
- paused = GhostNavigation.none
+ } else if (navigationState === GhostNavigation.backToOriginal) {
+ navigationState = GhostNavigation.none
Object.assign(modalState, state.modalState)
console.log('came from a ghost navigation, nothing to do')
// let's remove the guard from navigating away, it will be added again by afterEach when
}
if (!state) return
- // we came from a navigation that only changed the url
- // TODO: check the currentValue
- if (state.forwardGhost) {
+ // we did a back from a modal
+ if (state.forwardGhost && webHistory.state.ghostURL === state.forwardGhost) {
// make sure the url saved in the history stack is good
- paused = GhostNavigation.restoreGhostUrl
+ navigationState = GhostNavigation.restoreGhostUrl
+ cleanNavigationFromModalListener && cleanNavigationFromModalListener()
webHistory.forward(false)
- // we are coming from
+ // we did a forward to a modal
} else if (
- state.displayURL &&
- state.originalURL === router.currentRoute.value.fullPath
+ state.ghostURL &&
+ state.ghostURL === webHistory.state.forwardGhost
) {
webHistory.replace(state.displayURL)
event.stopImmediatePropagation()
Object.assign(modalState, state.modalState)
- // webHistory.replace(state.originalURL)
+ // TODO: setup same listeners as state S
+ // we did a back to a modal
+ } else if (
+ state.ghostURL &&
+ state.ghostURL === webHistory.state.backwardGhost
+ ) {
+ let remove = router.afterEach(() => {
+ Object.assign(modalState, state.modalState)
+ remove()
+ removeError()
+ })
+ // if the navigation fails, remove the listeners
+ let removeError = router.onError(() => {
+ console.log('navigation aborted, removing stuff')
+ remove()
+ removeError()
+ })
}
// if ((state && !state.forward) || state.showModal) {
// console.log('stopping it!')
}
let historyCleaner: (() => void) | undefined
+
+let cleanNavigationFromModalListener: (() => void) | undefined
+
+function setupPostNavigationFromModal(ghostURL: string) {
+ let removePost: (() => void) | undefined
+ const removeGuard = router.beforeEach((to, from, next) => {
+ console.log('From', from.fullPath, '->', to.fullPath)
+ // change the URL before leaving so that when we go back we are navigating to the right url
+ webHistory.replace(ghostURL)
+ console.log('changed url', ghostURL)
+ removeGuard()
+ removePost = router.afterEach(() => {
+ console.log('✅ navigated away')
+ webHistory.replace(webHistory.location, {
+ backwardGhost: ghostURL,
+ })
+ removePost && removePost()
+ })
+
+ // trigger the navigation again, TODO: does it change anything
+ next(to.fullPath)
+ })
+
+ // remove any existing listener
+ cleanNavigationFromModalListener && cleanNavigationFromModalListener()
+
+ cleanNavigationFromModalListener = () => {
+ removeGuard()
+ removePost && removePost()
+ cleanNavigationFromModalListener = undefined
+ }
+}
+
function showUserModal(id: number) {
const route = router.currentRoute.value
// generate a new entry that is exactly like the one we are on but with an extra query
// so it still counts like a navigation for the router when leaving it or when pushing on top
- const newURL = router.resolve({
+ const ghostURLNormalized = router.resolve({
path: route.path,
query: { ...route.query, __m: Math.random() },
hash: route.hash,
// the url we want to show
let url = router.resolve({ name: 'user', params: { id: '' + id } })
const displayURL = url.fullPath
- const ghostURL = newURL.fullPath
+ const ghostURL = ghostURLNormalized.fullPath
const originalURL = router.currentRoute.value.fullPath
webHistory.replace(router.currentRoute.value, {
// save that we are going to a ghost route
forwardGhost: ghostURL,
+ // save current modalState to be able to restore it when navigating away
+ // from the modal
modalState: { ...modalState },
})
+ // after saving the modal state, we can change it
modalState.userId = id
modalState.showModal = true
- // push a new entry in the history stack
- webHistory.push(newURL, {
- // the url we need to give the router to show
+ // push a new entry in the history stack with the ghost url in the state
+ // to be able to restore it
+ webHistory.push(displayURL, {
+ // the url that should be displayed while being on this entry
displayURL,
+ // the original url TODO: is it necessary?
originalURL,
+ // the url that resolves to the same components as originalURL but slightly different
+ // so that the router doesn't consider it as a duplicated navigation
ghostURL,
modalState: { ...modalState },
})
- // TODO: this can be one single call
-
- // display the correct url
- webHistory.replace(displayURL)
-
- // history.pushState({ showModal: true }, '', url)
- historyCleaner && historyCleaner()
// make sure we clear what we did before leaving
- historyCleaner = router.beforeEach((to, from, next) => {
- console.log('restoring the url', originalURL)
- // change the URL before leaving so that when we go back we are navigating to the right url
- webHistory.replace(ghostURL, {
- // save the current value of the modalState
- modalState: { ...modalState },
- })
- // remove this guard
- historyCleaner()
- let remove = router.afterEach(() => {
- webHistory.replace(router.currentRoute.value.fullPath, {
- previousGhost: ghostURL,
- })
- remove()
- })
- // hide the modal after saving the state
- modalState.showModal = false
- // trigger the same navigation again
- next(to.fullPath)
- })
+ // this will only trigger on `push`/`replace` because we are listening on `popstate`
+ // so that if we go to the previous entry we can stop the propagation so the router never knows
+ // and remove this listener ourselves
+ setupPostNavigationFromModal(ghostURL)
}
function closeUserModal() {