From: Eduardo San Martin Morote Date: Tue, 11 Jun 2019 10:41:40 +0000 (+0200) Subject: fix(history): fix pausing between back and forward X-Git-Tag: v4.0.0-alpha.0~344 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f10199a95da40e4ccbf729dfb5fcffcb01629305;p=thirdparty%2Fvuejs%2Frouter.git fix(history): fix pausing between back and forward 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 --- diff --git a/src/errors.ts b/src/errors.ts index 60666d43..df49b9d6 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -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( diff --git a/src/history/html5.ts b/src/history/html5.ts index 34bdffe0..661b2d5f 100644 --- a/src/history/html5.ts +++ b/src/history/html5.ts @@ -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 = () => {