Skip to content

Commit aed2510

Browse files
committed
Rerun LoopVideo if restored from BFCache
1 parent c49d7c2 commit aed2510

File tree

1 file changed

+53
-44
lines changed

1 file changed

+53
-44
lines changed

dotcom-rendering/src/components/LoopVideo.importable.tsx

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ export const LoopVideo = ({
110110
const [currentTime, setCurrentTime] = useState(0);
111111
const [playerState, setPlayerState] =
112112
useState<(typeof PLAYER_STATES)[number]>('NOT_STARTED');
113-
114113
const [isAutoplayAllowed, setIsAutoplayAllowed] = useState<boolean | null>(
115114
null,
116115
);
116+
const [isRestoredFromBFCache, setIsRestoredFromBFCache] = useState(false);
117117

118118
/**
119119
* Keep a track of whether the video has been in view. We only
@@ -190,31 +190,34 @@ export const LoopVideo = ({
190190
/>
191191
);
192192

193-
/**
194-
* Setup.
195-
*
196-
* 1. Register the user's motion preferences.
197-
* 2. Creates event listeners to control playback when there are multiple videos.
198-
*/
199-
useEffect(() => {
193+
const doesUserPermitAutoplay = (): boolean => {
200194
/**
201195
* The user indicates a preference for reduced motion: https://web.dev/articles/prefers-reduced-motion
202196
*/
203197
const userPrefersReducedMotion = window.matchMedia(
204198
'(prefers-reduced-motion: reduce)',
205199
).matches;
206200

207-
const autoplayPreference = storage.local.get(
208-
'gu.prefs.accessibility.autoplay-video',
209-
);
210201
/**
211-
* `autoplayPreference` is explicitly `false`
212-
* when the user has said they don't want autoplay video.
202+
* The user can set this on their Accessibility Settings page.
203+
* Explicitly `false` when the user has said they don't want autoplay video.
213204
*/
214-
setIsAutoplayAllowed(
215-
!userPrefersReducedMotion && autoplayPreference !== false,
205+
const autoplayPreference = storage.local.get(
206+
'gu.prefs.accessibility.autoplay-video',
216207
);
217208

209+
return !userPrefersReducedMotion && autoplayPreference !== false;
210+
};
211+
212+
/**
213+
* Setup.
214+
*
215+
* 1. Determine whether we can autoplay video.
216+
* 2. Creates event listeners to control playback when there are multiple videos.
217+
*/
218+
useEffect(() => {
219+
setIsAutoplayAllowed(doesUserPermitAutoplay());
220+
218221
/**
219222
* Mutes the current video when another video is unmuted
220223
* Triggered by the CustomEvent sent by each player on unmuting
@@ -308,18 +311,33 @@ export const LoopVideo = ({
308311
}, [isInView, hasBeenInView, atomId]);
309312

310313
/**
311-
* Autoplay the video when it comes into view.
314+
* Handle play/pause, when instigated by the browser.
312315
*/
313316
useEffect(() => {
314-
if (!vidRef.current || isAutoplayAllowed === false) {
317+
if (!vidRef.current || !isPlayable) {
318+
return;
319+
}
320+
321+
/**
322+
* Stops playback when the video is scrolled out of view.
323+
*/
324+
const isNoLongerInView =
325+
playerState === 'PLAYING' && hasBeenInView && isInView === false;
326+
if (isNoLongerInView) {
327+
pauseVideo('PAUSED_BY_INTERSECTION_OBSERVER');
315328
return;
316329
}
317330

331+
/**
332+
* Autoplay/resume playback when the player comes into view or when
333+
* the page has been restored from the BFCache.
334+
*/
318335
if (
336+
isAutoplayAllowed &&
319337
isInView &&
320-
isPlayable &&
321338
(playerState === 'NOT_STARTED' ||
322-
playerState === 'PAUSED_BY_INTERSECTION_OBSERVER')
339+
playerState === 'PAUSED_BY_INTERSECTION_OBSERVER' ||
340+
(isRestoredFromBFCache && playerState === 'PAUSED_BY_BROWSER'))
323341
) {
324342
/**
325343
* Check if the video has not been in view before tracking the play.
@@ -329,6 +347,7 @@ export const LoopVideo = ({
329347
ophanTrackerWeb(atomId, 'loop')('play');
330348
}
331349

350+
setIsRestoredFromBFCache(false);
332351
void playVideo();
333352
}
334353
}, [
@@ -338,34 +357,10 @@ export const LoopVideo = ({
338357
playerState,
339358
playVideo,
340359
hasBeenInView,
360+
isRestoredFromBFCache,
341361
atomId,
342362
]);
343363

344-
/**
345-
* Stops playback when the video is scrolled out of view.
346-
* Resumes playback when the video is back in the viewport.
347-
*/
348-
useEffect(() => {
349-
if (!vidRef.current || !hasBeenInView) return;
350-
351-
const isNoLongerInView =
352-
playerState === 'PLAYING' && isInView === false;
353-
if (isNoLongerInView) {
354-
pauseVideo('PAUSED_BY_INTERSECTION_OBSERVER');
355-
}
356-
357-
/**
358-
* If a user action paused the video, they have indicated
359-
* that they don't want to watch the video. Therefore, don't
360-
* resume the video when it comes back in view.
361-
*/
362-
const isBackInView =
363-
playerState === 'PAUSED_BY_INTERSECTION_OBSERVER' && isInView;
364-
if (isBackInView) {
365-
void playVideo();
366-
}
367-
}, [isInView, hasBeenInView, playerState, playVideo]);
368-
369364
/**
370365
* Show the play icon when the video is not playing, except for when it is scrolled
371366
* out of view. In this case, the intersection observer will resume playback and
@@ -404,6 +399,20 @@ export const LoopVideo = ({
404399
setPreloadPartialData(isAutoplayAllowed === false || !!isInView);
405400
}, [isAutoplayAllowed, isInView]);
406401

402+
/**
403+
* Handle the case where the user navigates back to the page.
404+
*/
405+
useEffect(() => {
406+
window.addEventListener('pageshow', function (event) {
407+
if (event.persisted) {
408+
setIsAutoplayAllowed(doesUserPermitAutoplay());
409+
setIsRestoredFromBFCache(true);
410+
} else {
411+
setIsRestoredFromBFCache(false);
412+
}
413+
});
414+
}, []);
415+
407416
if (renderingTarget !== 'Web') return null;
408417

409418
if (adapted) {

0 commit comments

Comments
 (0)