Skip to content

Commit

Permalink
♻️ [Story Ads] Move existing placement logic to algo impl (#33286)
Browse files Browse the repository at this point in the history
* move existing algo

* write tests

* no new

* change to number
  • Loading branch information
calebcordry committed Mar 17, 2021
1 parent 124d410 commit 2b16c5b
Show file tree
Hide file tree
Showing 2 changed files with 384 additions and 5 deletions.
115 changes: 110 additions & 5 deletions extensions/amp-story-auto-ads/0.1/algorithm-count-pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,129 @@
* limitations under the License.
*/

import {InsertionState} from './story-ad-page-manager';
import {StateProperty} from '../../amp-story/1.0/amp-story-store-service';
import {hasOwn, map} from '../../../src/utils/object';

/** @const {number} */
const INTERVAL = 7;

/**
* Original Story Ads placement algorithm. Tries to place ad every seven pages.
* Will not place if ad is still loading.
* @implements {./algorithm-interface.StoryAdPlacementAlgorithm}
*/
export class CountPagesAlgorithm {
/** @override */
constructor(unusedStoreService, unusedPageManager) {}
constructor(storeService, pageManager) {
/** @private {!../../amp-story/1.0/amp-story-store-service.AmpStoryStoreService} */
this.storeService_ = storeService;

/** @private {!StoryAdPageManager} */
this.pageManager_ = pageManager;

/** @private {!Object<string, boolean>} */
this.uniquePageIds_ = map();

/** @private {number} */
this.newPagesSinceLastAd_ = 1;

/** @private {boolean} */
this.pendingAdView_ = false;

/** @private {boolean} */
this.tryingToInsert_ = false;
}

/** @override */
isStoryEligible() {}
isStoryEligible() {
const numPages = this.storeService_.get(StateProperty.PAGE_IDS).length;
return numPages > INTERVAL;
}

/** @override */
initializePages() {}
initializePages() {
return [this.pageManager_.createAdPage()];
}

/** @override */
onPageChange(unusedPageId) {}
onPageChange(pageId) {
if (!hasOwn(this.uniquePageIds_, pageId)) {
this.uniquePageIds_[pageId] = true;
this.newPagesSinceLastAd_++;
}

if (
this.pendingAdView_ ||
this.tryingToInsert_ ||
!this.readyToPlaceAd_() ||
!this.pageManager_.hasUnusedAdPage()
) {
return;
}

this.tryingToInsert_ = true;
this.tryToPlaceAdAfterPage_(pageId);
}

/** @override */
onNewAdView(unusedPageIndex) {}
onNewAdView(pageIndex) {
this.pendingAdView_ = false;
this.newPagesSinceLastAd_ = 0;
if (this.shouldCreateNextAd_(pageIndex)) {
this.pageManager_.createAdPage();
}
}

/**
* Determine if enough pages in the story are left for ad placement to be
* possible.
*
* @param {number} pageIndex
* @return {boolean}
*/
shouldCreateNextAd_(pageIndex) {
const numPages = this.storeService_.get(StateProperty.PAGE_IDS).length;
return numPages - pageIndex > INTERVAL;
}

/**
* Determine if user has seen enough pages to show an ad. We want a certain
* number of pages before the first ad, and then a separate interval
* thereafter.
* @return {boolean}
*/
readyToPlaceAd_() {
return this.newPagesSinceLastAd_ >= INTERVAL;
}

/**
* Place ad based on user config.
* @param {string} pageBeforeAdId
* @private
*/
tryToPlaceAdAfterPage_(pageBeforeAdId) {
const nextAdPage = this.pageManager_.getUnusedAdPage();

// Timeout fail, move to next ad on next navigation.
if (!nextAdPage.isLoaded() && nextAdPage.hasTimedOut()) {
this.pageManager_.discardCurrentAd();
return;
}

// Keep trying the same ad, so we just exit without changing state.
if (!nextAdPage.isLoaded()) {
return;
}

this.pageManager_
.maybeInsertPageAfter(pageBeforeAdId, nextAdPage)
.then((insertionState) => {
this.tryingToInsert_ = false;
if (insertionState === InsertionState.SUCCESS) {
// We have an ad inserted that has yet to be viewed.
this.pendingAdView_ = true;
}
});
}
}

0 comments on commit 2b16c5b

Please sign in to comment.