@@ -143,10 +143,10 @@ export const LoopVideo = ({
143
143
const [ currentTime , setCurrentTime ] = useState ( 0 ) ;
144
144
const [ playerState , setPlayerState ] =
145
145
useState < ( typeof PLAYER_STATES ) [ number ] > ( 'NOT_STARTED' ) ;
146
-
147
146
const [ isAutoplayAllowed , setIsAutoplayAllowed ] = useState < boolean | null > (
148
147
null ,
149
148
) ;
149
+ const [ isRestoredFromBFCache , setIsRestoredFromBFCache ] = useState ( false ) ;
150
150
151
151
/**
152
152
* Keep a track of whether the video has been in view. We only
@@ -223,31 +223,34 @@ export const LoopVideo = ({
223
223
/>
224
224
) ;
225
225
226
- /**
227
- * Setup.
228
- *
229
- * 1. Register the user's motion preferences.
230
- * 2. Creates event listeners to control playback when there are multiple videos.
231
- */
232
- useEffect ( ( ) => {
226
+ const doesUserPermitAutoplay = ( ) : boolean => {
233
227
/**
234
228
* The user indicates a preference for reduced motion: https://web.dev/articles/prefers-reduced-motion
235
229
*/
236
230
const userPrefersReducedMotion = window . matchMedia (
237
231
'(prefers-reduced-motion: reduce)' ,
238
232
) . matches ;
239
233
240
- const autoplayPreference = storage . local . get (
241
- 'gu.prefs.accessibility.autoplay-video' ,
242
- ) ;
243
234
/**
244
- * `autoplayPreference` is explicitly `false`
245
- * when the user has said they don't want autoplay video.
235
+ * The user can set this on their Accessibility Settings page.
236
+ * Explicitly `false` when the user has said they don't want autoplay video.
246
237
*/
247
- setIsAutoplayAllowed (
248
- ! userPrefersReducedMotion && autoplayPreference !== false ,
238
+ const autoplayPreference = storage . local . get (
239
+ 'gu.prefs.accessibility.autoplay-video' ,
249
240
) ;
250
241
242
+ return ! userPrefersReducedMotion && autoplayPreference !== false ;
243
+ } ;
244
+
245
+ /**
246
+ * Setup.
247
+ *
248
+ * 1. Determine whether we can autoplay video.
249
+ * 2. Creates event listeners to control playback when there are multiple videos.
250
+ */
251
+ useEffect ( ( ) => {
252
+ setIsAutoplayAllowed ( doesUserPermitAutoplay ( ) ) ;
253
+
251
254
/**
252
255
* Mutes the current video when another video is unmuted
253
256
* Triggered by the CustomEvent sent by each player on unmuting
@@ -341,18 +344,33 @@ export const LoopVideo = ({
341
344
} , [ isInView , hasBeenInView , atomId ] ) ;
342
345
343
346
/**
344
- * Autoplay the video when it comes into view .
347
+ * Handle play/pause, when instigated by the browser .
345
348
*/
346
349
useEffect ( ( ) => {
347
- if ( ! vidRef . current || isAutoplayAllowed === false ) {
350
+ if ( ! vidRef . current || ! isPlayable ) {
351
+ return ;
352
+ }
353
+
354
+ /**
355
+ * Stops playback when the video is scrolled out of view.
356
+ */
357
+ const isNoLongerInView =
358
+ playerState === 'PLAYING' && hasBeenInView && isInView === false ;
359
+ if ( isNoLongerInView ) {
360
+ pauseVideo ( 'PAUSED_BY_INTERSECTION_OBSERVER' ) ;
348
361
return ;
349
362
}
350
363
364
+ /**
365
+ * Autoplay/resume playback when the player comes into view or when
366
+ * the page has been restored from the BFCache.
367
+ */
351
368
if (
369
+ isAutoplayAllowed &&
352
370
isInView &&
353
- isPlayable &&
354
371
( playerState === 'NOT_STARTED' ||
355
- playerState === 'PAUSED_BY_INTERSECTION_OBSERVER' )
372
+ playerState === 'PAUSED_BY_INTERSECTION_OBSERVER' ||
373
+ ( isRestoredFromBFCache && playerState === 'PAUSED_BY_BROWSER' ) )
356
374
) {
357
375
/**
358
376
* Check if the video has not been in view before tracking the play.
@@ -362,6 +380,7 @@ export const LoopVideo = ({
362
380
ophanTrackerWeb ( atomId , 'loop' ) ( 'play' ) ;
363
381
}
364
382
383
+ setIsRestoredFromBFCache ( false ) ;
365
384
void playVideo ( ) ;
366
385
}
367
386
} , [
@@ -371,34 +390,10 @@ export const LoopVideo = ({
371
390
playerState ,
372
391
playVideo ,
373
392
hasBeenInView ,
393
+ isRestoredFromBFCache ,
374
394
atomId ,
375
395
] ) ;
376
396
377
- /**
378
- * Stops playback when the video is scrolled out of view.
379
- * Resumes playback when the video is back in the viewport.
380
- */
381
- useEffect ( ( ) => {
382
- if ( ! vidRef . current || ! hasBeenInView ) return ;
383
-
384
- const isNoLongerInView =
385
- playerState === 'PLAYING' && isInView === false ;
386
- if ( isNoLongerInView ) {
387
- pauseVideo ( 'PAUSED_BY_INTERSECTION_OBSERVER' ) ;
388
- }
389
-
390
- /**
391
- * If a user action paused the video, they have indicated
392
- * that they don't want to watch the video. Therefore, don't
393
- * resume the video when it comes back in view.
394
- */
395
- const isBackInView =
396
- playerState === 'PAUSED_BY_INTERSECTION_OBSERVER' && isInView ;
397
- if ( isBackInView ) {
398
- void playVideo ( ) ;
399
- }
400
- } , [ isInView , hasBeenInView , playerState , playVideo ] ) ;
401
-
402
397
/**
403
398
* Show the play icon when the video is not playing, except for when it is scrolled
404
399
* out of view. In this case, the intersection observer will resume playback and
@@ -437,6 +432,20 @@ export const LoopVideo = ({
437
432
setPreloadPartialData ( isAutoplayAllowed === false || ! ! isInView ) ;
438
433
} , [ isAutoplayAllowed , isInView ] ) ;
439
434
435
+ /**
436
+ * Handle the case where the user navigates back to the page.
437
+ */
438
+ useEffect ( ( ) => {
439
+ window . addEventListener ( 'pageshow' , function ( event ) {
440
+ if ( event . persisted ) {
441
+ setIsAutoplayAllowed ( doesUserPermitAutoplay ( ) ) ;
442
+ setIsRestoredFromBFCache ( true ) ;
443
+ } else {
444
+ setIsRestoredFromBFCache ( false ) ;
445
+ }
446
+ } ) ;
447
+ } , [ ] ) ;
448
+
440
449
if ( renderingTarget !== 'Web' ) return null ;
441
450
442
451
if ( adapted ) {
0 commit comments