Skip to content

Commit 30705d8

Browse files
authored
Squash commits for: tap to next/previous Story. (ampproject#22684)
1 parent dc980b9 commit 30705d8

File tree

5 files changed

+179
-25
lines changed

5 files changed

+179
-25
lines changed

extensions/amp-story/1.0/amp-story-page.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,11 @@ export class AmpStoryPage extends AMP.BaseElement {
374374
this.state_ = state;
375375
break;
376376
case PageState.PAUSED:
377-
this.advancement_.stop(true /** canResume */);
377+
// canResume keeps the time advancement timer if set to true, and resets
378+
// it when set to false. If the bookend if open, reset the timer. If
379+
// user is long pressing, don't reset it.
380+
const canResume = !this.storeService_.get(StateProperty.BOOKEND_STATE);
381+
this.advancement_.stop(canResume);
378382
this.pauseAllMedia_(false /** rewindToBeginning */);
379383
if (this.animationManager_) {
380384
this.animationManager_.pauseAll();
@@ -1077,20 +1081,20 @@ export class AmpStoryPage extends AMP.BaseElement {
10771081
* Navigates to the previous page in the story.
10781082
*/
10791083
previous() {
1080-
const targetPageId = this.getPreviousPageId();
1084+
const pageId = this.getPreviousPageId();
10811085

1082-
if (targetPageId === null) {
1086+
if (pageId === null) {
10831087
dispatch(
10841088
this.win,
10851089
this.element,
1086-
EventType.SHOW_NO_PREVIOUS_PAGE_HELP,
1090+
EventType.NO_PREVIOUS_PAGE,
10871091
/* payload */ undefined,
10881092
{bubbles: true}
10891093
);
10901094
return;
10911095
}
10921096

1093-
this.switchTo_(targetPageId, NavigationDirection.PREVIOUS);
1097+
this.switchTo_(pageId, NavigationDirection.PREVIOUS);
10941098
}
10951099

10961100
/**
@@ -1102,6 +1106,13 @@ export class AmpStoryPage extends AMP.BaseElement {
11021106
const pageId = this.getNextPageId(isAutomaticAdvance);
11031107

11041108
if (!pageId) {
1109+
dispatch(
1110+
this.win,
1111+
this.element,
1112+
EventType.NO_NEXT_PAGE,
1113+
/* payload */ undefined,
1114+
{bubbles: true}
1115+
);
11051116
return;
11061117
}
11071118

extensions/amp-story/1.0/amp-story-store-service.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export const Action = {
162162
TOGGLE_ACCESS: 'toggleAccess',
163163
TOGGLE_AD: 'toggleAd',
164164
TOGGLE_BOOKEND: 'toggleBookend',
165+
TOGGLE_CAN_SHOW_BOOKEND: 'toggleCanShowBookend',
165166
TOGGLE_HAS_SIDEBAR: 'toggleHasSidebar',
166167
TOGGLE_INFO_DIALOG: 'toggleInfoDialog',
167168
TOGGLE_INTERACTIVE_COMPONENT: 'toggleInteractiveComponent',
@@ -247,6 +248,10 @@ const actions = (state, action, data) => {
247248
[StateProperty.BOOKEND_STATE]: !!data,
248249
[StateProperty.PAUSED_STATE]: !!data,
249250
}));
251+
case Action.TOGGLE_CAN_SHOW_BOOKEND:
252+
return /** @type {!State} */ (Object.assign({}, state, {
253+
[StateProperty.CAN_SHOW_BOOKEND]: !!data,
254+
}));
250255
case Action.TOGGLE_INTERACTIVE_COMPONENT:
251256
data = /** @type {InteractiveComponentDef} */ (data);
252257
return /** @type {!State} */ (Object.assign({}, state, {

extensions/amp-story/1.0/amp-story.js

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,11 @@ export class AmpStory extends AMP.BaseElement {
427427

428428
this.storeService_.dispatch(Action.TOGGLE_UI, this.getUIType_());
429429

430+
// Disables the bookend entirely if the story is within a group of stories.
431+
if (this.viewer_.hasCapability('swipe')) {
432+
this.storeService_.dispatch(Action.TOGGLE_CAN_SHOW_BOOKEND, false);
433+
}
434+
430435
this.navigationState_.observe(stateChangeEvent => {
431436
this.variableService_.onNavigationStateChange(stateChangeEvent);
432437
this.analyticsService_.onNavigationStateChange(stateChangeEvent);
@@ -676,10 +681,12 @@ export class AmpStory extends AMP.BaseElement {
676681
this.replay_();
677682
});
678683

679-
this.element.addEventListener(EventType.SHOW_NO_PREVIOUS_PAGE_HELP, () => {
680-
if (this.storeService_.get(StateProperty.CAN_SHOW_PREVIOUS_PAGE_HELP)) {
681-
this.ampStoryHint_.showFirstPageHintOverlay();
682-
}
684+
this.element.addEventListener(EventType.NO_NEXT_PAGE, () => {
685+
this.onNoNextPage_();
686+
});
687+
688+
this.element.addEventListener(EventType.NO_PREVIOUS_PAGE, () => {
689+
this.onNoPreviousPage_();
683690
});
684691

685692
this.advancement_.addOnTapNavigationListener(direction => {
@@ -1223,21 +1230,24 @@ export class AmpStory extends AMP.BaseElement {
12231230
this.activePage_,
12241231
'No active page set when navigating to next page.'
12251232
);
1233+
activePage.next(opt_isAutomaticAdvance);
1234+
}
12261235

1227-
const lastPage = this.pages_[this.getPageCount() - 1];
1228-
if (
1229-
activePage.element.hasAttribute(Attributes.ADVANCE_TO) ||
1230-
activePage.element.hasAttribute(Attributes.PUBLIC_ADVANCE_TO) ||
1231-
activePage !== lastPage
1232-
) {
1233-
activePage.next(opt_isAutomaticAdvance);
1234-
} else {
1235-
this.hasBookend_().then(hasBookend => {
1236-
if (hasBookend) {
1237-
this.showBookend_();
1238-
}
1239-
});
1236+
/**
1237+
* Handles EventType.NO_NEXT_PAGE events.
1238+
* @private
1239+
*/
1240+
onNoNextPage_() {
1241+
if (this.viewer_.hasCapability('swipe')) {
1242+
this.viewer_./*OK*/ sendMessage('selectDocument', dict({'next': true}));
1243+
return;
12401244
}
1245+
1246+
this.hasBookend_().then(hasBookend => {
1247+
if (hasBookend) {
1248+
this.showBookend_();
1249+
}
1250+
});
12411251
}
12421252

12431253
/**
@@ -1252,6 +1262,24 @@ export class AmpStory extends AMP.BaseElement {
12521262
activePage.previous();
12531263
}
12541264

1265+
/**
1266+
* Handles EventType.NO_PREVIOUS_PAGE events.
1267+
* @private
1268+
*/
1269+
onNoPreviousPage_() {
1270+
if (this.viewer_.hasCapability('swipe')) {
1271+
this.viewer_./*OK*/ sendMessage(
1272+
'selectDocument',
1273+
dict({'previous': true})
1274+
);
1275+
return;
1276+
}
1277+
1278+
if (this.storeService_.get(StateProperty.CAN_SHOW_PREVIOUS_PAGE_HELP)) {
1279+
this.ampStoryHint_.showFirstPageHintOverlay();
1280+
}
1281+
}
1282+
12551283
/**
12561284
* @param {number} direction The direction to navigate.
12571285
* @private
@@ -2253,7 +2281,10 @@ export class AmpStory extends AMP.BaseElement {
22532281
* @private
22542282
*/
22552283
maybePreloadBookend_() {
2256-
if (!this.activePage_) {
2284+
if (
2285+
!this.activePage_ ||
2286+
!this.storeService_.get(StateProperty.CAN_SHOW_BOOKEND)
2287+
) {
22572288
return;
22582289
}
22592290

extensions/amp-story/1.0/events.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ export const EventType = {
4343
// warnings or errors).
4444
DEV_LOG_ENTRIES_AVAILABLE: 'ampstory:devlogentriesavailable',
4545

46-
// Triggered when user clicks on left 25% of the first page
47-
SHOW_NO_PREVIOUS_PAGE_HELP: 'ampstory:shownopreviouspagehelp',
46+
// Triggered when user clicks on end 75% of the last page
47+
NO_NEXT_PAGE: 'ampstory:nonextpage',
48+
49+
// Triggered when user clicks on start 25% of the first page
50+
NO_PREVIOUS_PAGE: 'ampstory:nopreviouspage',
4851

4952
// Triggered when a story has loaded at least its initial set of pages.
5053
STORY_LOADED: 'ampstory:load',

extensions/amp-story/1.0/test/test-amp-story.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {PageState} from '../amp-story-page';
3434
import {PaginationButtons} from '../pagination-buttons';
3535
import {Services} from '../../../../src/services';
3636
import {createElementWithAttributes} from '../../../../src/dom';
37+
import {poll} from '../../../../testing/iframe';
3738
import {registerServiceBuilder} from '../../../../src/service';
3839
import {toggleExperiment} from '../../../../src/experiments';
3940

@@ -86,10 +87,23 @@ describes.realWin(
8687
return eventObj;
8788
}
8889

90+
function waitFor(callback, errorMessage) {
91+
return poll(
92+
errorMessage,
93+
() => {
94+
return callback();
95+
},
96+
undefined /** opt_onError */,
97+
200 /** opt_timeout */
98+
);
99+
}
100+
89101
beforeEach(() => {
90102
win = env.win;
91103

92104
replaceStateStub = sandbox.stub(win.history, 'replaceState');
105+
// Required by the bookend code.
106+
win.document.title = 'Story';
93107

94108
const viewer = Services.viewerForDoc(env.ampdoc);
95109
sandbox
@@ -1117,6 +1131,96 @@ describes.realWin(
11171131
});
11181132
});
11191133

1134+
describe('amp-story NO_NEXT_PAGE', () => {
1135+
describe('without #cap=swipe', () => {
1136+
it('should open the bookend when tapping on the last page', () => {
1137+
createPages(story.element, 1, ['cover']);
1138+
1139+
return story.layoutCallback().then(() => {
1140+
// Click on right side of the screen to trigger page advancement.
1141+
const clickEvent = new MouseEvent('click', {clientX: 200});
1142+
story.activePage_.element.dispatchEvent(clickEvent);
1143+
return waitFor(() => {
1144+
return !!story.storeService_.get(StateProperty.BOOKEND_STATE);
1145+
}, 'BOOKEND_STATE should be true');
1146+
});
1147+
});
1148+
});
1149+
1150+
describe('with #cap=swipe', () => {
1151+
before(() => (hasSwipeCapability = true));
1152+
after(() => (hasSwipeCapability = false));
1153+
1154+
it('should send a message when tapping on last page in viewer', () => {
1155+
createPages(story.element, 1, ['cover']);
1156+
const sendMessageStub = sandbox.stub(story.viewer_, 'sendMessage');
1157+
1158+
return story.layoutCallback().then(() => {
1159+
// Click on right side of the screen to trigger page advancement.
1160+
const clickEvent = new MouseEvent('click', {clientX: 200});
1161+
story.activePage_.element.dispatchEvent(clickEvent);
1162+
return waitFor(() => {
1163+
if (sendMessageStub.calledOnce) {
1164+
expect(sendMessageStub).to.be.calledWithExactly(
1165+
'selectDocument',
1166+
{next: true}
1167+
);
1168+
return true;
1169+
}
1170+
return false;
1171+
}, 'sendMessageStub should be called');
1172+
});
1173+
});
1174+
});
1175+
});
1176+
1177+
describe('amp-story NO_PREVIOUS_PAGE', () => {
1178+
describe('without #cap=swipe', () => {
1179+
it('should open the bookend when tapping on the last page', () => {
1180+
createPages(story.element, 1, ['cover']);
1181+
const showPageHintStub = sandbox.stub(
1182+
story.ampStoryHint_,
1183+
'showFirstPageHintOverlay'
1184+
);
1185+
1186+
return story.layoutCallback().then(() => {
1187+
// Click on left side of the screen to trigger page advancement.
1188+
const clickEvent = new MouseEvent('click', {clientX: 10});
1189+
story.activePage_.element.dispatchEvent(clickEvent);
1190+
return waitFor(() => {
1191+
return showPageHintStub.calledOnce;
1192+
}, 'showPageHintStub should be called');
1193+
});
1194+
});
1195+
});
1196+
1197+
describe('with #cap=swipe', () => {
1198+
before(() => (hasSwipeCapability = true));
1199+
after(() => (hasSwipeCapability = false));
1200+
1201+
it('should send a message when tapping on last page in viewer', () => {
1202+
createPages(story.element, 1, ['cover']);
1203+
const sendMessageStub = sandbox.stub(story.viewer_, 'sendMessage');
1204+
1205+
return story.layoutCallback().then(() => {
1206+
// Click on left side of the screen to trigger page advancement.
1207+
const clickEvent = new MouseEvent('click', {clientX: 10});
1208+
story.activePage_.element.dispatchEvent(clickEvent);
1209+
return waitFor(() => {
1210+
if (sendMessageStub.calledOnce) {
1211+
expect(sendMessageStub).to.be.calledWithExactly(
1212+
'selectDocument',
1213+
{previous: true}
1214+
);
1215+
return true;
1216+
}
1217+
return false;
1218+
}, 'sendMessageStub should be called');
1219+
});
1220+
});
1221+
});
1222+
});
1223+
11201224
describe('amp-story navigation', () => {
11211225
it('should navigate when performing a navigational click', () => {
11221226
createPages(story.element, 4, ['cover', 'page-1', 'page-2', 'page-3']);

0 commit comments

Comments
 (0)