Skip to content

Commit b8d4d30

Browse files
authored
Rewind last story page on visibilitystate inactive. (ampproject#29704)
1 parent ba861e0 commit b8d4d30

File tree

2 files changed

+74
-8
lines changed

2 files changed

+74
-8
lines changed

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {SwipeXYRecognizer} from '../../../src/gesture-recognizers';
7676
import {SystemLayer} from './amp-story-system-layer';
7777
import {UnsupportedBrowserLayer} from './amp-story-unsupported-browser-layer';
7878
import {ViewportWarningLayer} from './amp-story-viewport-warning-layer';
79+
import {VisibilityState} from '../../../src/visibility-state';
7980
import {
8081
childElement,
8182
childElementByTag,
@@ -342,10 +343,10 @@ export class AmpStory extends AMP.BaseElement {
342343

343344
/**
344345
* Store the current paused state, to make sure the story does not play on
345-
* resume if it was previously paused.
346-
* @private {boolean}
346+
* resume if it was previously paused. null when nothing to restore.
347+
* @private {?boolean}
347348
*/
348-
this.pausedStateToRestore_ = false;
349+
this.pausedStateToRestore_ = null;
349350

350351
/** @private {?Element} */
351352
this.sidebar_ = null;
@@ -467,13 +468,22 @@ export class AmpStory extends AMP.BaseElement {
467468
* @private
468469
*/
469470
pause_() {
470-
this.pausedStateToRestore_ = !!this.storeService_.get(
471-
StateProperty.PAUSED_STATE
472-
);
471+
// Preserve if previously set. This method can be called several times when
472+
// setting the visibilitystate to paused and then inactive.
473+
if (this.pausedStateToRestore_ === null) {
474+
this.pausedStateToRestore_ = !!this.storeService_.get(
475+
StateProperty.PAUSED_STATE
476+
);
477+
}
473478
this.storeService_.dispatch(Action.TOGGLE_PAUSED, true);
474479
if (!this.storeService_.get(StateProperty.MUTED_STATE)) {
475480
this.pauseBackgroundAudio_();
476481
}
482+
// If viewer has navigated to the next document, reset the active page.
483+
if (this.getAmpDoc().getVisibilityState() === VisibilityState.INACTIVE) {
484+
this.activePage_.setState(PageState.NOT_ACTIVE);
485+
this.activePage_.element.setAttribute('active', '');
486+
}
477487
}
478488

479489
/**
@@ -486,6 +496,7 @@ export class AmpStory extends AMP.BaseElement {
486496
Action.TOGGLE_PAUSED,
487497
this.pausedStateToRestore_
488498
);
499+
this.pausedStateToRestore_ = null;
489500
if (!this.storeService_.get(StateProperty.MUTED_STATE)) {
490501
this.playBackgroundAudio_();
491502
}
@@ -1812,6 +1823,7 @@ export class AmpStory extends AMP.BaseElement {
18121823
Action.TOGGLE_PAUSED,
18131824
this.pausedStateToRestore_
18141825
);
1826+
this.pausedStateToRestore_ = null;
18151827
this.storeService_.dispatch(Action.TOGGLE_VIEWPORT_WARNING, false);
18161828
}
18171829
});
@@ -2109,6 +2121,7 @@ export class AmpStory extends AMP.BaseElement {
21092121
Action.TOGGLE_PAUSED,
21102122
this.pausedStateToRestore_
21112123
);
2124+
this.pausedStateToRestore_ = null;
21122125
this.mutateElement(() => {
21132126
this.unsupportedBrowserLayer_.removeLayer();
21142127
});

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

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,17 @@ describes.realWin(
729729
expect(story.storeService_.get(StateProperty.PAUSED_STATE)).to.be.true;
730730
});
731731

732+
it('should rewind the story page when viewer becomes inactive', async () => {
733+
await createStoryWithPages(2, ['cover', 'page-1']);
734+
735+
await story.layoutCallback();
736+
const setStateStub = window.sandbox.stub(story.activePage_, 'setState');
737+
story.getAmpDoc().overrideVisibilityState(VisibilityState.INACTIVE);
738+
expect(setStateStub.getCall(1)).to.have.been.calledWithExactly(
739+
PageState.NOT_ACTIVE
740+
);
741+
});
742+
732743
it('should pause the story when viewer becomes hidden', async () => {
733744
await createStoryWithPages(2, ['cover', 'page-1']);
734745

@@ -737,6 +748,17 @@ describes.realWin(
737748
expect(story.storeService_.get(StateProperty.PAUSED_STATE)).to.be.true;
738749
});
739750

751+
it('should pause the story page when viewer becomes hidden', async () => {
752+
await createStoryWithPages(2, ['cover', 'page-1']);
753+
754+
await story.layoutCallback();
755+
const setStateStub = window.sandbox.stub(story.activePage_, 'setState');
756+
story.getAmpDoc().overrideVisibilityState(VisibilityState.HIDDEN);
757+
expect(setStateStub).to.have.been.calledOnceWithExactly(
758+
PageState.PAUSED
759+
);
760+
});
761+
740762
it('should pause the story when viewer becomes paused', async () => {
741763
await createStoryWithPages(2, ['cover', 'page-1']);
742764

@@ -756,7 +778,7 @@ describes.realWin(
756778
);
757779
});
758780

759-
it('should play the story when viewer becomes active', async () => {
781+
it('should play the story when viewer becomes active after paused', async () => {
760782
await createStoryWithPages(2, ['cover', 'page-1']);
761783

762784
await story.layoutCallback();
@@ -765,14 +787,33 @@ describes.realWin(
765787
expect(story.storeService_.get(StateProperty.PAUSED_STATE)).to.be.false;
766788
});
767789

768-
it('should play the story page when viewer becomes active', async () => {
790+
it('should play the story page when viewer becomes active after paused', async () => {
791+
await createStoryWithPages(2, ['cover', 'page-1']);
792+
793+
await story.layoutCallback();
794+
const setStateStub = window.sandbox.stub(story.activePage_, 'setState');
795+
story.getAmpDoc().overrideVisibilityState(VisibilityState.PAUSED);
796+
story.getAmpDoc().overrideVisibilityState(VisibilityState.ACTIVE);
797+
expect(setStateStub.getCall(1)).to.have.been.calledWithExactly(
798+
PageState.PLAYING
799+
);
800+
});
801+
802+
it('should play the story page when viewer becomes active after paused + inactive', async () => {
769803
await createStoryWithPages(2, ['cover', 'page-1']);
770804

771805
await story.layoutCallback();
772806
const setStateStub = window.sandbox.stub(story.activePage_, 'setState');
773807
story.getAmpDoc().overrideVisibilityState(VisibilityState.PAUSED);
808+
story.getAmpDoc().overrideVisibilityState(VisibilityState.INACTIVE);
774809
story.getAmpDoc().overrideVisibilityState(VisibilityState.ACTIVE);
810+
expect(setStateStub.getCall(0)).to.have.been.calledWithExactly(
811+
PageState.PAUSED
812+
);
775813
expect(setStateStub.getCall(1)).to.have.been.calledWithExactly(
814+
PageState.NOT_ACTIVE
815+
);
816+
expect(setStateStub.getCall(2)).to.have.been.calledWithExactly(
776817
PageState.PLAYING
777818
);
778819
});
@@ -788,6 +829,18 @@ describes.realWin(
788829
expect(story.storeService_.get(StateProperty.PAUSED_STATE)).to.be.true;
789830
});
790831

832+
it('should keep the story paused on resume when previously paused + inactive', async () => {
833+
await createStoryWithPages(2, ['cover', 'page-1']);
834+
835+
story.storeService_.dispatch(Action.TOGGLE_PAUSED, true);
836+
837+
await story.layoutCallback();
838+
story.getAmpDoc().overrideVisibilityState(VisibilityState.PAUSED);
839+
story.getAmpDoc().overrideVisibilityState(VisibilityState.INACTIVE);
840+
story.getAmpDoc().overrideVisibilityState(VisibilityState.ACTIVE);
841+
expect(story.storeService_.get(StateProperty.PAUSED_STATE)).to.be.true;
842+
});
843+
791844
describe('amp-story continue anyway', () => {
792845
it('should not display layout', async () => {
793846
await createStoryWithPages(2, ['cover', 'page-4']);

0 commit comments

Comments
 (0)