]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
fix(history): fix pausing between back and forward
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 11 Jun 2019 10:41:40 +0000 (12:41 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 11 Jun 2019 10:41:40 +0000 (12:41 +0200)
I realized calling history.forward() on Safari didn't immediately called
the popstate listener, so simply setting a variable before and after
history.back() and history.forward() wasn't enough.
It still doesn't work consistently between all browsers. eg: on Safari
calling history.back() mulitple times in a row will trigger only one
popstate listener but on Firefox, it will trigger as many as necessary

src/errors.ts
src/history/html5.ts

index 60666d4353f530af9454c6ee05ac551a49ab2a0c..df49b9d66764806825b674691b8736448c1d419b 100644 (file)
@@ -27,8 +27,10 @@ export class InvalidRouteMatch extends Error {
  * information about where we where trying to go and where we are going instead
  */
 export class NavigationGuardRedirect extends Error {
-  from: RouteLocationNormalized
   to: RouteLocation
+  from: RouteLocationNormalized
+  // TODO: refactor order of argumnets
+  // TODO: refactor into parent class NavigationError
   constructor(from: RouteLocationNormalized, to: RouteLocation) {
     super(
       `Redirected from "${from.fullPath}" to "${stringifyRoute(
index 34bdffe01fe58780973ebbdccb4894eeb1d74ea7..661b2d5f94b134373be9e387f321429a6e17a0dc 100644 (file)
@@ -33,12 +33,22 @@ function buildState(
   }
 }
 
+interface PauseState {
+  currentLocation: HistoryLocationNormalized
+  // location we are going to after pausing
+  to: HistoryLocationNormalized
+}
+
 export class HTML5History extends BaseHistory {
   private history = window.history
   private _popStateHandler: PopStateListener
   private _listeners: NavigationCallback[] = []
   private _teardowns: Array<() => void> = []
 
+  // TODO: should it be a stack? a Dict. Check if the popstate listener
+  // can trigger twice
+  private pauseState: PauseState | null = null
+
   constructor() {
     super()
     const to = buildFullPath()
@@ -92,17 +102,20 @@ export class HTML5History extends BaseHistory {
   }
 
   back(triggerListeners: boolean = true) {
-    const paused = this.paused
-    if (!triggerListeners) this.paused = true
+    // TODO: check if we can go back
+    const previvousLocation = this.history.state
+      .back as HistoryLocationNormalized
+    if (!triggerListeners) this.pauseListeners(previvousLocation)
     this.history.back()
-    this.paused = paused
   }
 
   forward(triggerListeners: boolean = true) {
-    const paused = this.paused
-    if (!triggerListeners) this.paused = true
+    // TODO: check if we can go forward
+    const previvousLocation = this.history.state
+      .forward as HistoryLocationNormalized
+    if (!previvousLocation) throw new Error('Cannot go forward')
+    if (!triggerListeners) this.pauseListeners(previvousLocation)
     this.history.forward()
-    this.paused = paused
   }
 
   listen(callback: NavigationCallback) {
@@ -138,14 +151,25 @@ export class HTML5History extends BaseHistory {
         state,
         location: this.location,
       })
-      if (this.paused) {
-        cs.info('Ignored beacuse paused')
-        return
-      }
+
+      // TODO: handle go(-2) and go(2) (skipping entries)
+
       const from = this.location
       // we have the state from the old entry, not the current one being removed
       // TODO: correctly parse pathname
-      this.location = state ? state.current : buildFullPath()
+      const to = state ? state.current : buildFullPath()
+      this.location = to
+
+      if (
+        this.pauseState &&
+        this.pauseState.to &&
+        this.pauseState.to.fullPath === to.fullPath
+      ) {
+        cs.info('Ignored beacuse paused')
+        // reset pauseState
+        this.pauseState = null
+        return
+      }
 
       // call all listeners
       const navigationInfo = {
@@ -163,6 +187,13 @@ export class HTML5History extends BaseHistory {
     window.addEventListener('popstate', handler)
     return handler
   }
+
+  private pauseListeners(to: HistoryLocationNormalized) {
+    this.pauseState = {
+      currentLocation: this.location,
+      to,
+    }
+  }
 }
 
 const buildFullPath = () => {