@@ -103,12 +103,14 @@ import {createPseudoLocale} from '../../../src/localized-strings';
103103import { debounce } from '../../../src/utils/rate-limit' ;
104104import { dev , devAssert , user } from '../../../src/log' ;
105105import { dict , map } from '../../../src/utils/object' ;
106+ import { endsWith } from '../../../src/string' ;
106107import { escapeCssSelectorIdent } from '../../../src/css' ;
107108import { findIndex } from '../../../src/utils/array' ;
108109import { getConsentPolicyState } from '../../../src/consent' ;
109110import { getDetail } from '../../../src/event-helper' ;
110111import { getMediaQueryService } from './amp-story-media-query-service' ;
111112import { getMode } from '../../../src/mode' ;
113+ import { getState } from '../../../src/history' ;
112114import { isExperimentOn } from '../../../src/experiments' ;
113115import { parseQueryString } from '../../../src/url' ;
114116import { registerServiceBuilder } from '../../../src/service' ;
@@ -437,13 +439,20 @@ export class AmpStory extends AMP.BaseElement {
437439 if ( isExperimentOn ( this . win , 'amp-story-branching' ) ) {
438440 this . registerAction ( 'goToPage' , invocation => {
439441 const { args} = invocation ;
440- if ( args ) {
441- this . storeService_ . dispatch (
442- Action . SET_ADVANCEMENT_MODE ,
443- AdvancementMode . GO_TO_PAGE
444- ) ;
445- this . switchTo_ ( args [ 'id' ] , NavigationDirection . NEXT ) ;
442+ if ( ! args ) {
443+ return ;
446444 }
445+ this . storeService_ . dispatch (
446+ Action . SET_ADVANCEMENT_MODE ,
447+ AdvancementMode . GO_TO_PAGE
448+ ) ;
449+ // If open, closes the sidebar before navigating.
450+ const promise = this . storeService_ . get ( StateProperty . SIDEBAR_STATE )
451+ ? Services . historyForDoc ( this . getAmpDoc ( ) ) . goBack ( )
452+ : Promise . resolve ( ) ;
453+ promise . then ( ( ) =>
454+ this . switchTo_ ( args [ 'id' ] , NavigationDirection . NEXT )
455+ ) ;
447456 } ) ;
448457 }
449458 }
@@ -787,6 +796,29 @@ export class AmpStory extends AMP.BaseElement {
787796
788797 this . getAmpDoc ( ) . onVisibilityChanged ( ( ) => this . onVisibilityChanged_ ( ) ) ;
789798
799+ if ( isExperimentOn ( this . win , 'amp-story-branching' ) ) {
800+ this . win . addEventListener ( 'hashchange' , ( ) => {
801+ const maybePageId = parseQueryString ( this . win . location . hash ) [ 'page' ] ;
802+ if ( ! maybePageId || ! this . isActualPage_ ( maybePageId ) ) {
803+ return ;
804+ }
805+ this . switchTo_ ( maybePageId , NavigationDirection . NEXT ) ;
806+ // Removes the page 'hash' parameter from the URL.
807+ let href = this . win . location . href . replace (
808+ new RegExp ( `page=${ maybePageId } &?` ) ,
809+ ''
810+ ) ;
811+ if ( endsWith ( href , '#' ) ) {
812+ href = href . slice ( 0 , - 1 ) ;
813+ }
814+ this . win . history . replaceState (
815+ ( this . win . history && getState ( this . win . history ) ) || { } /** data */ ,
816+ this . win . document . title /** title */ ,
817+ href /** URL */
818+ ) ;
819+ } ) ;
820+ }
821+
790822 this . getViewport ( ) . onResize ( debounce ( this . win , ( ) => this . onResize ( ) , 300 ) ) ;
791823 this . installGestureRecognizers_ ( ) ;
792824 }
@@ -1049,27 +1081,38 @@ export class AmpStory extends AMP.BaseElement {
10491081 * @private
10501082 */
10511083 getInitialPageId_ ( firstPageEl ) {
1052- const isActualPage = pageId =>
1053- ! ! this . element . querySelector ( `#${ escapeCssSelectorIdent ( pageId ) } ` ) ;
10541084 const historyPage = /** @type {string } */ ( getHistoryState (
10551085 this . win ,
10561086 HistoryState . PAGE_ID
10571087 ) ) ;
10581088
10591089 if ( isExperimentOn ( this . win , 'amp-story-branching' ) ) {
10601090 const maybePageId = parseQueryString ( this . win . location . hash ) [ 'page' ] ;
1061- if ( maybePageId && isActualPage ( maybePageId ) ) {
1091+ if ( maybePageId && this . isActualPage_ ( maybePageId ) ) {
10621092 return maybePageId ;
10631093 }
10641094 }
10651095
1066- if ( historyPage && isActualPage ( historyPage ) ) {
1096+ if ( historyPage && this . isActualPage_ ( historyPage ) ) {
10671097 return historyPage ;
10681098 }
10691099
10701100 return firstPageEl . id ;
10711101 }
10721102
1103+ /**
1104+ * Checks if the amp-story-page for a given ID exists.
1105+ * Note: the `this.pages_` array might not be defined yet.
1106+ * @param {string } pageId
1107+ * @return {boolean }
1108+ * @private
1109+ */
1110+ isActualPage_ ( pageId ) {
1111+ // TODO(gmajoulet): check from the cached pages array if available, and use
1112+ // the querySelector as a fallback.
1113+ return ! ! this . element . querySelector ( `#${ escapeCssSelectorIdent ( pageId ) } ` ) ;
1114+ }
1115+
10731116 /**
10741117 * @param {number } timeoutMs The maximum amount of time to wait, in
10751118 * milliseconds.
0 commit comments