-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
馃悰Ensure that goToPage actions and fragment Urls work in sidebar for stories #20944
Changes from all commits
ee49a21
02cd367
1d7fb9c
8dc93d5
3b149cc
d33f448
74dacc3
ffa60db
8f935ee
91a3f4d
47978de
efddc95
1e53480
587e05f
c691147
7ec27b4
93d0a06
7b91cb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,6 +84,7 @@ import { | |
childElementByTag, | ||
childElements, | ||
closest, | ||
closestAncestorElementBySelector, | ||
createElementWithAttributes, | ||
isRTL, | ||
scopedQuerySelectorAll, | ||
|
@@ -109,6 +110,7 @@ import {getMode} from '../../../src/mode'; | |
import {isExperimentOn} from '../../../src/experiments'; | ||
import {parseQueryString} from '../../../src/url'; | ||
import {registerServiceBuilder} from '../../../src/service'; | ||
import {startsWith} from '../../../src/string'; | ||
import {upgradeBackgroundAudio} from './audio'; | ||
import LocalizedStringsAr from './_locales/ar'; | ||
import LocalizedStringsDe from './_locales/de'; | ||
|
@@ -438,13 +440,35 @@ export class AmpStory extends AMP.BaseElement { | |
|
||
if (isExperimentOn(this.win, 'amp-story-branching')) { | ||
this.registerAction('goToPage', invocation => { | ||
const {args} = invocation; | ||
const {args, caller} = invocation; | ||
|
||
if (args) { | ||
this.storeService_.dispatch( | ||
Action.SET_ADVANCEMENT_MODE, | ||
AdvancementMode.GO_TO_PAGE | ||
); | ||
this.switchTo_(args['id'], NavigationDirection.NEXT); | ||
|
||
if ( | ||
closestAncestorElementBySelector( | ||
dev().assertElement(caller), | ||
'AMP-SIDEBAR' | ||
) | ||
) { | ||
const pageId = args['id']; | ||
Services.historyForDoc(this.getAmpDoc()) | ||
.goBack() | ||
.then(() => { | ||
this.win.requestAnimationFrame(() => { | ||
if (this.isActualPage_(pageId)) { | ||
this.switchTo_(pageId, NavigationDirection.NEXT).then(() => | ||
this.closeOpacityMask_() | ||
); | ||
} | ||
}); | ||
}); | ||
} else { | ||
this.switchTo_(args['id'], NavigationDirection.NEXT); | ||
} | ||
} | ||
}); | ||
} | ||
|
@@ -750,6 +774,24 @@ export class AmpStory extends AMP.BaseElement { | |
} | ||
}); | ||
|
||
if (isExperimentOn(this.win, 'amp-story-branching')) { | ||
this.win.addEventListener('hashchange', () => { | ||
const maybePageId = parseQueryString(this.win.location.hash)['page']; | ||
if (this.isActualPage_(maybePageId)) { | ||
this.switchTo_(maybePageId, NavigationDirection.NEXT).then(() => { | ||
this.closeOpacityMask_(); | ||
}); | ||
// Remove the fragment parameter from the URL | ||
const {history} = this.win; | ||
history.pushState( | ||
/* data */ '', | ||
/* title */ this.win.document.title, | ||
/* URL */ this.win.location.pathname | ||
); | ||
} | ||
}); | ||
} | ||
|
||
// TODO(#16795): Remove once the runtime triggers pause/resume callbacks | ||
// on document visibility change (eg: user switches tab). | ||
this.documentState_.onVisibilityChanged(() => this.onVisibilityChanged_()); | ||
|
@@ -1016,27 +1058,35 @@ export class AmpStory extends AMP.BaseElement { | |
* @private | ||
*/ | ||
getInitialPageId_(firstPageEl) { | ||
const isActualPage = pageId => | ||
findIndex(this.pages_, page => page.element.id === pageId) >= 0; | ||
const historyPage = /** @type {string} */ (getHistoryState( | ||
this.win, | ||
HistoryState.PAGE_ID | ||
)); | ||
|
||
if (isExperimentOn(this.win, 'amp-story-branching')) { | ||
const maybePageId = parseQueryString(this.win.location.hash)['page']; | ||
if (maybePageId && isActualPage(maybePageId)) { | ||
if (maybePageId && this.isActualPage_(maybePageId)) { | ||
return maybePageId; | ||
} | ||
} | ||
|
||
if (historyPage && isActualPage(historyPage)) { | ||
if (historyPage && this.isActualPage_(historyPage)) { | ||
return historyPage; | ||
} | ||
|
||
return firstPageEl.id; | ||
} | ||
|
||
/** | ||
* Checks to see if a page ID refers to an actual page in the story. | ||
* @param {string} pageId | ||
* @return {boolean} | ||
* @private | ||
*/ | ||
isActualPage_(pageId) { | ||
return findIndex(this.pages_, page => page.element.id === pageId) >= 0; | ||
} | ||
|
||
/** | ||
* @param {number} timeoutMs The maximum amount of time to wait, in | ||
* milliseconds. | ||
|
@@ -2504,6 +2554,28 @@ export class AmpStory extends AMP.BaseElement { | |
if (!this.sidebar_) { | ||
return; | ||
} | ||
if (isExperimentOn(this.win, 'amp-story-branching')) { | ||
const linkEls = Array.prototype.slice.call( | ||
this.sidebar_.querySelectorAll('a') | ||
); | ||
|
||
linkEls.forEach(linkEl => { | ||
const url = linkEl.getAttribute('href'); | ||
// Handles both anchor links (#page=someId) and absolute links; click | ||
// handler should not be added to absolute links from same domain but | ||
// different story. | ||
if ( | ||
url.indexOf('#page=') >= 0 && | ||
(startsWith(url, '#') || url.indexOf(this.win.location.pathname) >= 0) | ||
) { | ||
linkEl.addEventListener('click', () => { | ||
// Do not prevent default; in safari, preventing default and | ||
// appending a hash to a URL without a preceding slash fails. | ||
this.win.location.hash = url.slice(url.search('#(.*)')); | ||
}); | ||
} | ||
}); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A few things about this block:
Could we do all that from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We took this offline a little, but done! Utilized hashchange handler for fragments, but not for goToPage. I also added a check for the pathname, to handler for href values that look like |
||
|
||
this.mutateElement(() => { | ||
this.sidebar_.classList.add(SIDEBAR_CLASS_NAME); | ||
|
@@ -2512,12 +2584,15 @@ export class AmpStory extends AMP.BaseElement { | |
this.initializeOpacityMask_(); | ||
this.storeService_.dispatch(Action.TOGGLE_HAS_SIDEBAR, !!this.sidebar_); | ||
|
||
const actions = [ | ||
const sidebarActions = [ | ||
{tagOrTarget: 'AMP-SIDEBAR', method: 'open'}, | ||
{tagOrTarget: 'AMP-SIDEBAR', method: 'close'}, | ||
{tagOrTarget: 'AMP-SIDEBAR', method: 'toggle'}, | ||
]; | ||
this.storeService_.dispatch(Action.ADD_TO_ACTIONS_WHITELIST, actions); | ||
this.storeService_.dispatch( | ||
Action.ADD_TO_ACTIONS_WHITELIST, | ||
sidebarActions | ||
); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is introducing a bug. Let's imagine this story has a third page:
(I haven't tried this exact scenario, but from the history state bug this should happen)
When the sidebar opens, it pushes a new history entry. Because you're using a promise to get the sidebar implementation, the
sidebarImpl.close()
will happen after theswitchTo
.switchTo
writes the history state in the entry added by the sidebar, that is then popped bysidebarImpl.close()
. So the navigation path actually never got updated, resulting in the navigation bug described above.An easy fix would be to call
switchTo
after the sidebar's history entry is popped.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Synched offline but you're right that there was a historyState bug, but it actually corrects itself as you navigate forwards.
With the changes I just pushed, when you navigate using a sidebar link (not action), checking the
ampStoryNavigationPath
will yield an undefined state, but that also corrects itself when you either navigates forwards or backwards (this is due to manipulating the history state to not show the fragment as the user is navigating via the sidebar links/actions).