Skip to content
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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added page URL fragment parameter #20238

Merged
merged 20 commits into from Jan 18, 2019
Merged

Added page URL fragment parameter #20238

merged 20 commits into from Jan 18, 2019

Conversation

bramanudom
Copy link
Contributor

#20083

A fragment parameter should be added to a story url, which should allow users to open (and eventually share) a story at a specific page.

maybeInitialPageId = this.maybeChangeInitial_(initialPageId);
}
this.switchTo_(maybeInitialPageId, NavigationDirection.NEXT);
})
Copy link
Contributor

@gmajoulet gmajoulet Jan 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chaining promises is pretty confusing, but here you're actually changing the way this code behaves.

The this.switchTo_() method returns a promise, that resolves when the page navigation happened.

The way the code was written was using an arrow function, with an implicit return
() => 'foo' is the shorthand for () => {return 'foo';}
What it means is that the way it was written before, it was returning the Promise from the switchTo_ method, and the next then() callback wasn't executed until the Promise was resolved (navigation happens).
Your new code doesn't return that Promise, which means that the following code will run before it's actually resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for catching this and for taking the time to explain everything out! I made changes, but will definitely keep in mind the implicit return and be more perceptive about chained promises in the future.

* @return {string}
* @private
*/
maybeChangeInitial_(initialPageId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With these changes, the way we get the initial page ID is now done in two steps:

  1. const initialPageId = this.getHistoryState_(HistoryStates.PAGE_ID) || firstPageEl.id;
  2. Later, maybe changing it

What do you think of trying to do this in only one step? It might make things easier.
Like, where we get the initialPageId right now, we could have a getInitialPageId_() method, that would try to get the page ID in that order:

  1. If there is a page ID in the history, use it (if a user refreshes their page, they'd expect to end up on the same page)
  2. If there's a page ID in the URL fragment, use it
  3. Fallback to the first page of the story

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me! I went ahead and made the changes. maybeChangeInitial_ is now getInitialPageId and retrieves the correct pageId in the order you described. There's some code left because this is still behind an experiment wall but I mark that as a TODO to be removed explicitly.

@@ -1328,6 +1328,28 @@ describes.realWin('amp-story', {
expect(story.activePage_.element.id).to.equal('cover');
});
});
it('should begin at the specified page fragment parameter value', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: newline before this it() and the one below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

@cvializ cvializ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, deferring to @gmajoulet and @Enriqe

@@ -764,6 +765,8 @@ export class AmpStory extends AMP.BaseElement {
this.element.querySelector('amp-story-page'),
'Story must have at least one page.');

// TODO(#20128): initialPageId will no longer be necessary here once
// branching experiment is live.
const initialPageId = this.getHistoryState_(HistoryStates.PAGE_ID) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call const initialPageId = this.getInitialPageId_(firstPageEl); directly here, and do the experiment check in that method? This way, we could remove that block before the switchTo_, and keep the code like it was before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that getInitialPageId_ needs to be called later because at this point the pages_ array hasn't been initialized yet.

How do feel about getting rid of the assignment of initialPageId entirely, still moving the experiment check to getInitialPageID_, and calling it in switchTo_ directly as the first argument?

* Retrieves the initial pageId to begin the story with. In order, the
* initial page for a story should be the page ID in the history, a valid
* page ID in the URL fragment, and then the first page of the story.
* @param {Element} firstPageEl
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: !Element

/**
* Retrieves the initial pageId to begin the story with. In order, the
* initial page for a story should be the page ID in the history, a valid
* page ID in the URL fragment, and then the first page of the story.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With your implementation, the page ID in the URL hash will actually override anything else. But I think it's fine, and more aligned with other Google products. For example, on most Google products, a hl=language parameter in a URL would override any local preference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to param taking precedence over history state

const maybePageId = parseQueryString(this.win.location.hash)['page'];
const isActualPage =
findIndex(this.pages_, page => page.element.id === maybePageId) >= 0;
return (maybePageId && isActualPage) ? maybePageId : initialPageId;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also check that the page ID from the history state exists? This is a bug we're tracking too.

Can we use getPageIndexById instead, to avoid code duplication?
I was thinking wrapping these calls in try/catch statements might allow us to use this method (does it?), and still notify the developer that there is something wrong with what they're doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm a little confused here. By checking that the page ID from the history state exists, did you mean something more explicit than just this.getHistoryState_(HistoryStates.PAGE_ID) || firstPageEl.id?

Also, I don't think we can use a try/catch here because of how user().error() works. Just to double check I went ahead and tried, and code in the catch block does not execute despite getPageIndexById returning a negative number.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, that line of code checks if there is something in the history (this.getHistoryState_(HistoryStates.PAGE_ID)), and if not (if that first expression is falsy), it fallbacks the ID of the first page of the story (firstPageEl.id).

We're getting a page ID from the history, but just like the page ID from the URL, we can't be sure that the story actually has a page for that ID. Because you just wrote some code to check this, I was wondering if we could also use it to check that the page ID from the history points to an existing page?

if (maybePageId && isActualPage(maybePageId)) {
return maybePageId;
}
} else if (historyPage && isActualPage(historyPage)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to get the page from the history even if the branching experiment is on :))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, thank you for catching this!

if (maybePageId && isActualPage(maybePageId)) {
return maybePageId;
}
} if (historyPage && isActualPage(historyPage)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plz fix the indent, and let's merge this :)) Thanks for implementing the comments!

@bramanudom
Copy link
Contributor Author

@gmajoulet I believe this is ready for either continued review or for merging! 😀

@gmajoulet gmajoulet merged commit 304b48b into ampproject:master Jan 18, 2019
noranazmy pushed a commit to noranazmy/amphtml that referenced this pull request Mar 22, 2019
* parse URl fragment parameter for starting page of the story

* cleaned up code and fixed logic

* fixed tests! made it harder for myself unnecessarily

* removed logging and linted

* removed logging and linted

* changed to array util findIndex

* addressed PR comments

* edit JSDoc to reflect changes, added explicit todo

* addressed PR comments

* removed private getHistoryState_

* historystate not historystates :'(

* missing arg

* allow usage of history PageID when not branching

* allow usage of history PageID when not branching
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants