Skip to content

Commit

Permalink
✨amp-next-page v2 history manipulation (ampproject#25971)
Browse files Browse the repository at this point in the history
* Added intial implementation of history manipulation on amp-next-page v2

* Added ability to go back to the initial page and fixed logic

* Fixes type checks and adds og:image ot the initial page

* Feedback from review

* Switched to using an object instead of params in page.js
  • Loading branch information
wassgha authored and Micajuine Ho committed Dec 27, 2019
1 parent 1da9404 commit 125702c
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 33 deletions.
44 changes: 27 additions & 17 deletions extensions/amp-next-page/0.2/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,35 @@ export const PageRelativePos = {
export class Page {
/**
* @param {!./service.NextPageService} manager
* @param {string} url
* @param {string} title
* @param {string} image
* @param {{ url: string, title: string, image: string }} meta
* @param {!PageState=} initState
* @param {!VisibilityState=} initVisibility
*/
constructor(manager, url, title, image) {
constructor(
manager,
meta,
initState = PageState.QUEUED,
initVisibility = VisibilityState.PRERENDER
) {
/** @private @const {!./service.NextPageService} */
this.manager_ = manager;
/** @private @const {string} */
this.title_ = title;
this.title_ = meta.title;
/** @private {string} */
this.url_ = url;
this.url_ = meta.url;
/** @private @const {string} */
this.image_ = image;
this.image_ = meta.image;

/** @private {?../../../src/runtime.ShadowDoc} */
this.shadowDoc_ = null;
/** @private {!PageState} */
this.state_ = PageState.QUEUED;
this.state_ = initState;
/** @private {?RelativePositions} */
this.headerPosition_ = null;
/** @private {?RelativePositions} */
this.footerPosition_ = null;
/** @private {!VisibilityState} */
this.visibilityState_ = VisibilityState.PRERENDER;
this.visibilityState_ = initVisibility;
/** @private {!PageRelativePos} */
this.relativePos_ = PageRelativePos.OUTSIDE_VIEWPORT;
}
Expand Down Expand Up @@ -112,14 +117,19 @@ export class Page {
/**
* @param {VisibilityState} visibilityState
*/
setVisible(visibilityState) {
// TODO(wassgha): Handle history manipulation
// TODO(wassgha): Handle manual visibility management

setVisibility(visibilityState) {
if (visibilityState == this.visibilityState_) {
return;
}
// Update visibility internally and at the shadow doc level
if (this.shadowDoc_ && visibilityState != this.visibilityState_) {
this.visibilityState_ = visibilityState;
if (this.shadowDoc_) {
this.shadowDoc_.setVisibilityState(visibilityState);
this.visibilityState_ = visibilityState;
}

// Switch the title and url of the page to reflect this page
if (visibilityState === VisibilityState.VISIBLE) {
this.manager_.setTitlePage(this);
}
}

Expand Down Expand Up @@ -229,8 +239,8 @@ export class Page {
// is looking at a section of the document
this.relativePos_ = PageRelativePos.CONTAINS_VIEWPORT;
} else if (
(header === TOP && footer === TOP) ||
(header === BOTTOM && footer === BOTTOM)
((!header || header === TOP) && footer === TOP) ||
(header === BOTTOM && (!footer || footer === BOTTOM))
) {
// Both the header and the footer of the document are either
// above or below the document meaning that the viewport hasn't
Expand Down
109 changes: 93 additions & 16 deletions extensions/amp-next-page/0.2/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import {CSS} from '../../../build/amp-next-page-0.2.css';
import {MultidocManager} from '../../../src/multidoc-manager';
import {Page, PageRelativePos} from './page';
import {Page, PageRelativePos, PageState} from './page';
import {
PositionObserver, // eslint-disable-line no-unused-vars
installPositionObserverServiceForDoc,
Expand All @@ -32,6 +32,11 @@ import {
} from '../../../src/dom';
import {dev, devAssert, user, userAssert} from '../../../src/log';
import {installStylesForDoc} from '../../../src/style-installer';
import {
parseFavicon,
parseOgImage,
parseSchemaImage,
} from '../../../src/mediasession-helper';
import {sanitizeDoc, validatePage, validateUrl} from './utils';
import {tryParseJson} from '../../../src/json';

Expand All @@ -44,9 +49,9 @@ export const Direction = {UP: 1, DOWN: -1};
export class NextPageService {
/**
* @param {!../../../src/service/ampdoc-impl.AmpDoc} ampdoc
* @param {!PositionObserver=} opt_injectedPositionObserver
* @param {!PositionObserver=} injectedPositionObserver
*/
constructor(ampdoc, opt_injectedPositionObserver) {
constructor(ampdoc, injectedPositionObserver) {
/** @private @const {!../../../src/service/ampdoc-impl.AmpDoc} */
this.ampdoc_ = ampdoc;

Expand All @@ -69,11 +74,14 @@ export class NextPageService {
this.element_ = null;

/** @private @const {?PositionObserver} */
this.injectedPositionObserver_ = opt_injectedPositionObserver || null;
this.injectedPositionObserver_ = injectedPositionObserver || null;

/** @private {?MultidocManager} */
this.multidocManager_ = null;

/** @private {?../../../src/service/history-impl.History} */
this.history_ = null;

/** @private {?Array<!Page>} */
this.pages_;

Expand All @@ -85,6 +93,9 @@ export class NextPageService {

/** @private {number} */
this.lastScrollTop_ = 0;

/** @private {?Page} */
this.initialPage_ = null;
}

/**
Expand All @@ -100,6 +111,12 @@ export class NextPageService {
* @param {!AmpElement} element
*/
build(element) {
// Create a reference to the host page
this.initialPage_ = this.createInitialPage();

this.history_ = Services.historyForDoc(this.ampdoc_);
this.initializeHistory();

this.multidocManager_ = new MultidocManager(
this.win_,
Services.ampdocServiceFor(this.win_),
Expand All @@ -115,13 +132,16 @@ export class NextPageService {
this.element_.appendChild(this.moreBox_);

if (!this.pages_) {
this.pages_ = [];
this.pages_ = [this.initialPage_];
this.setLastFetchedPage(this.initialPage_);
}

this.getPagesPromise_().then(pages => {
pages.forEach(page => {
validatePage(page, this.ampdoc_.getUrl());
this.pages_.push(new Page(this, page.url, page.title, page.image));
this.pages_.push(
new Page(this, {url: page.url, title: page.title, image: page.image})
);
});
});

Expand Down Expand Up @@ -176,23 +196,34 @@ export class NextPageService {
page.relativePos === PageRelativePos.CONTAINS_VIEWPORT
) {
if (!page.isVisible()) {
page.setVisible(VisibilityState.VISIBLE);
page.setVisibility(VisibilityState.VISIBLE);
}
// Hide the previous page
const prevPage = this.pages_[index + this.scrollDirection_];
if (
prevPage &&
prevPage.relativePos === PageRelativePos.LEAVING_VIEWPORT &&
prevPage.isVisible()
) {
prevPage.setVisible(VisibilityState.HIDDEN);
// Hide the previous pages
let prevPageIndex = index + this.scrollDirection_;
while (prevPageIndex >= 0 && prevPageIndex < this.pages_.length) {
const prevPage = this.pages_[prevPageIndex];
if (
prevPage &&
(prevPage.relativePos === PageRelativePos.LEAVING_VIEWPORT ||
prevPage.relativePos === PageRelativePos.OUTSIDE_VIEWPORT ||
prevPage === this.initialPage_) &&
prevPage.isVisible()
) {
prevPage.setVisibility(VisibilityState.HIDDEN);
}
prevPageIndex += this.scrollDirection_;
}
} else if (page.relativePos === PageRelativePos.OUTSIDE_VIEWPORT) {
if (page.isVisible()) {
page.setVisible(VisibilityState.VISIBLE);
page.setVisibility(VisibilityState.HIDDEN);
}
}
});

// If no page is visible then the host page should be
if (!this.pages_.some(page => page.isVisible())) {
this.initialPage_.setVisibility(VisibilityState.VISIBLE);
}
}

/**
Expand All @@ -216,6 +247,52 @@ export class NextPageService {
this.lastFetchedPage_ = page;
}

/**
* Sets the title and url of the document to those of
* the provided page
* @param {?Page=} page
*/
setTitlePage(page = this.initialPage_) {
if (!page) {
dev().warn(TAG, 'setTitlePage called before next-page-service is built');
return;
}
const {title, url} = page;
this.win_.document.title = title;
this.history_.replace({title, url});
}

/**
* Adds an initial entry in history that sub-pages can
* replace when they become visible
*/
initializeHistory() {
const {title, url} = this.initialPage_;
this.history_.push(undefined /** opt_onPop */, {title, url});
}

/**
*
* @return {!Page}
*/
createInitialPage() {
const doc = this.win_.document;
const {title, location} = doc;
const {href: url} = location;
const image =
parseSchemaImage(doc) || parseOgImage(doc) || parseFavicon(doc) || '';
return new Page(
this,
{
url,
title,
image,
},
PageState.INSERTED /** initState */,
VisibilityState.VISIBLE /** initVisibility */
);
}

/**
*
* @param {!Page} page
Expand Down

0 comments on commit 125702c

Please sign in to comment.