From 3afede51c9ff9ccc3f036494d0f13ba65ae5dfc9 Mon Sep 17 00:00:00 2001 From: Tanner Fleming Date: Thu, 14 May 2026 20:56:13 +0000 Subject: [PATCH 1/2] fix(resources): add optional chaining on images/imageAlt in VideoCard to prevent crash on minority languages When language switcher navigates to a language with no images or imageAlt data, direct array index access [0].prop threw TypeError. Add optional chaining so empty arrays render gracefully instead of crashing the Error Boundary. Fixes WAT-204 --- .../NewVideoContentPage/VideoCarousel/VideoCard/VideoCard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/resources/src/components/NewVideoContentPage/VideoCarousel/VideoCard/VideoCard.tsx b/apps/resources/src/components/NewVideoContentPage/VideoCarousel/VideoCard/VideoCard.tsx index df7cea12966..13354cf7a68 100644 --- a/apps/resources/src/components/NewVideoContentPage/VideoCarousel/VideoCard/VideoCard.tsx +++ b/apps/resources/src/components/NewVideoContentPage/VideoCarousel/VideoCard/VideoCard.tsx @@ -56,8 +56,8 @@ export function VideoCard({
{video.imageAlt[0].value} Date: Thu, 21 May 2026 21:43:51 +0000 Subject: [PATCH 2/2] fix: language switcher crash on copied video variants --- .../VideoControls/VideoControls.spec.tsx | 24 ++++ .../VideoControls/VideoControls.tsx | 34 ++++-- .../VideoControls/VideoControls.spec.tsx | 23 +++- .../VideoControls/VideoControls.tsx | 103 +++++++++++------- .../VideoControls/VideoControls.tsx | 83 +++++++++----- 5 files changed, 184 insertions(+), 83 deletions(-) diff --git a/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.spec.tsx b/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.spec.tsx index fcf1091dc17..045f984f250 100644 --- a/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.spec.tsx +++ b/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.spec.tsx @@ -315,6 +315,30 @@ describe('VideoControls', () => { }) }) + it('does not read volume from a disposed player', async () => { + player.dispose() + + expect(() => + render( + + + + + + + + + + ) + ).not.toThrow() + }) + it('updates volume on volumechange', async () => { vi.spyOn(player, 'volume').mockReturnValue(0) diff --git a/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.tsx b/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.tsx index 9c93300e1e9..ba817fc81e9 100644 --- a/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.tsx +++ b/apps/resources/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoControls/VideoControls.tsx @@ -57,6 +57,14 @@ interface VideoControlProps { onVisibleChanged?: (active: boolean) => void } +function isPlayerReady(player?: Player): player is Player { + return player != null && !player.isDisposed() +} + +function getPlayerVolume(player?: Player): number { + return isPlayerReady(player) ? (player.volume() ?? 1) * 100 : 100 +} + function evtToDataLayer( eventType, mcId, @@ -220,7 +228,7 @@ export function VideoControls({ useEffect(() => { dispatchPlayer({ type: 'SetVolume', - volume: (player?.volume() ?? 1) * 100 + volume: getPlayerVolume(player) }) player?.on('play', () => { if ((player?.currentTime() ?? 0) < 0.02) { @@ -290,17 +298,19 @@ export function VideoControls({ player?.on('volumechange', () => { dispatchPlayer({ type: 'SetMute', - mute: player?.muted() ?? false + mute: isPlayerReady(player) ? (player.muted() ?? false) : false }) dispatchPlayer({ type: 'SetVolume', - volume: (player?.volume() ?? 1) * 100 + volume: getPlayerVolume(player) }) }) player?.on('fullscreenchange', () => { dispatchPlayer({ type: 'SetFullscreen', - fullscreen: player?.isFullscreen() ?? false + fullscreen: isPlayerReady(player) + ? (player.isFullscreen() ?? false) + : false }) }) player?.on('useractive', () => @@ -391,9 +401,9 @@ export function VideoControls({ function handlePlay(): void { if (!play) { - void player?.play() + if (isPlayerReady(player)) void player.play() } else { - void player?.pause() + if (isPlayerReady(player)) void player.pause() } } @@ -406,7 +416,7 @@ export function VideoControls({ }) } else { if (isMobile()) { - void player?.requestFullscreen() + if (isPlayerReady(player)) void player.requestFullscreen() dispatchPlayer({ type: 'SetFullscreen', fullscreen: true @@ -427,12 +437,12 @@ export function VideoControls({ type: 'SetProgress', progress: value }) - player?.currentTime(value) + if (isPlayerReady(player)) player.currentTime(value) } } function handleMute(): void { - player?.muted(!mute) + if (isPlayerReady(player)) player.muted(!mute) dispatchPlayer({ type: 'SetMute', mute: !mute @@ -446,7 +456,7 @@ export function VideoControls({ type: 'SetVolume', volume: value }) - player?.volume(value / 100) + if (isPlayerReady(player)) player.volume(value / 100) } } @@ -494,7 +504,7 @@ export function VideoControls({ onClick={getClickHandler(handlePlay, () => { void handleFullscreen() })} - onMouseMove={() => player?.userActive(true)} + onMouseMove={() => isPlayerReady(player) && player.userActive(true)} data-testid="VideoControls" > {!loading ? ( @@ -532,7 +542,7 @@ export function VideoControls({ {images[0]?.mobileCinematicHigh != null && ( {imageAlt[0].value} { @@ -46,6 +47,26 @@ describe('VideoControls', () => { wasUnmuted: true } + it('does not read volume from a disposed player', () => { + const disposedPlayer = { + ...mockPlayer, + isDisposed: jest.fn().mockReturnValue(true), + volume: jest.fn(() => { + throw new Error('disposed') + }) + } as unknown as Player + + expect(() => + render( + + + + + + ) + ).not.toThrow() + }) + it('should render video controls', () => { render( diff --git a/apps/watch/src/components/VideoBlock/VideoBlockPlayer/VideoControls/VideoControls.tsx b/apps/watch/src/components/VideoBlock/VideoBlockPlayer/VideoControls/VideoControls.tsx index 4c234b4c667..3d3afc45e9a 100644 --- a/apps/watch/src/components/VideoBlock/VideoBlockPlayer/VideoControls/VideoControls.tsx +++ b/apps/watch/src/components/VideoBlock/VideoBlockPlayer/VideoControls/VideoControls.tsx @@ -71,6 +71,25 @@ interface VideoControlProps { wasUnmuted?: boolean } +function isPlayerReady(player?: Player): player is Player { + return ( + player != null && + (typeof player.isDisposed !== 'function' || !player.isDisposed()) + ) +} + +function getPlayerCurrentTime(player?: Player): number { + return isPlayerReady(player) ? (player.currentTime() ?? 0) : 0 +} + +function getPlayerDuration(player?: Player): number { + return isPlayerReady(player) ? (player.duration() ?? 1) : 1 +} + +function getPlayerVolume(player?: Player): number { + return isPlayerReady(player) ? (player.volume() ?? 1) * 100 : 100 +} + function evtToDataLayer( eventType, mcId, @@ -185,7 +204,9 @@ export function VideoControls({ let retryTimeout: NodeJS.Timeout | undefined const updateDuration = (state: string): void => { - const playerDuration = player?.duration() + const playerDuration = isPlayerReady(player) + ? player.duration() + : undefined if ( playerDuration != null && @@ -248,7 +269,7 @@ export function VideoControls({ useEffect(() => { const percent = durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 if (percent > progressPercentNotYetEmitted[0]) { eventToDataLayer( @@ -258,7 +279,7 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), percent ) const [, ...rest] = progressPercentNotYetEmitted @@ -278,7 +299,7 @@ export function VideoControls({ // Define stable event handlers using useCallback const handlePlay = useCallback(() => { - if ((player?.currentTime() ?? 0) < 0.02) { + if (getPlayerCurrentTime(player) < 0.02) { eventToDataLayer( 'video_start', id, @@ -286,9 +307,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } else { @@ -299,13 +320,13 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } - void player?.play() + if (isPlayerReady(player)) void player.play() dispatchPlayer({ type: 'SetPlay', play: true @@ -313,7 +334,7 @@ export function VideoControls({ }, [player, id, variant, title, durationSeconds, dispatchPlayer]) const handlePause = useCallback(() => { - if ((player?.currentTime() ?? 0) > 0.02) { + if (getPlayerCurrentTime(player) > 0.02) { eventToDataLayer( 'video_pause', id, @@ -321,13 +342,13 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } - player?.pause() + if (isPlayerReady(player)) player.pause() dispatchPlayer({ type: 'SetPlay', play: false @@ -342,7 +363,7 @@ export function VideoControls({ play: true }) // Analytics for player events - if ((player?.currentTime() ?? 0) < 0.02) { + if (getPlayerCurrentTime(player) < 0.02) { eventToDataLayer( 'video_start', id, @@ -350,9 +371,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } else { @@ -363,16 +384,16 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } }, [player, id, variant, title, durationSeconds, dispatchPlayer]) const handlePlayerEventPause = useCallback(() => { - if (!(player?.paused() ?? false)) { + if (!isPlayerReady(player) || !(player.paused() ?? false)) { // Firefox occasionally emits pause events even though playback continues; // ignore them so we don't thrash global player state. return @@ -383,7 +404,7 @@ export function VideoControls({ play: false }) // Analytics for player events - if ((player?.currentTime() ?? 0) > 0.02) { + if (getPlayerCurrentTime(player) > 0.02) { eventToDataLayer( 'video_pause', id, @@ -391,9 +412,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } @@ -402,7 +423,7 @@ export function VideoControls({ const handleTimeUpdate = useCallback(() => { dispatchPlayer({ type: 'SetCurrentTime', - currentTime: secondsToTimeFormat(player?.currentTime() ?? 0, { + currentTime: secondsToTimeFormat(getPlayerCurrentTime(player), { trimZeroes: true }) }) @@ -410,7 +431,7 @@ export function VideoControls({ type: 'SetProgress', progress: durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 }) }, [player, durationSeconds, dispatchPlayer]) @@ -418,18 +439,20 @@ export function VideoControls({ const handleVolumeChange = useCallback(() => { dispatchPlayer({ type: 'SetMute', - mute: player?.muted() ?? false + mute: isPlayerReady(player) ? (player.muted() ?? false) : false }) dispatchPlayer({ type: 'SetVolume', - volume: (player?.volume() ?? 1) * 100 + volume: getPlayerVolume(player) }) }, [player, dispatchPlayer]) const handleFullscreenChange = useCallback(() => { dispatchPlayer({ type: 'SetFullscreen', - fullscreen: player?.isFullscreen() ?? false + fullscreen: isPlayerReady(player) + ? (player.isFullscreen() ?? false) + : false }) }, [player, dispatchPlayer]) @@ -483,9 +506,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) }, [player, id, variant, title, durationSeconds]) @@ -523,7 +546,7 @@ export function VideoControls({ useEffect(() => { dispatchPlayer({ type: 'SetVolume', - volume: (player?.volume() ?? 1) * 100 + volume: getPlayerVolume(player) }) // Attach handlers - use separate handlers for player events vs user interactions @@ -550,9 +573,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } else { @@ -563,9 +586,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), durationSeconds > 0 - ? Math.round(((player?.currentTime() ?? 0) / durationSeconds) * 100) + ? Math.round((getPlayerCurrentTime(player) / durationSeconds) * 100) : 0 ) } @@ -630,7 +653,7 @@ export function VideoControls({ }) } else { if (isMobile()) { - void player?.requestFullscreen() + if (isPlayerReady(player)) void player.requestFullscreen() dispatchPlayer({ type: 'SetFullscreen', fullscreen: true @@ -663,7 +686,7 @@ export function VideoControls({ type: 'SetProgress', progress: progressPercent }) - player?.currentTime(timeInSeconds) + if (isPlayerReady(player)) player.currentTime(timeInSeconds) } } @@ -675,7 +698,7 @@ export function VideoControls({ function handleMute(): void { const newMuteState = !mute - player?.muted(newMuteState) + if (isPlayerReady(player)) player.muted(newMuteState) dispatchPlayer({ type: 'SetMute', mute: newMuteState @@ -692,7 +715,7 @@ export function VideoControls({ type: 'SetVolume', volume: value }) - player?.volume(value / 100) + if (isPlayerReady(player)) player.volume(value / 100) } } @@ -738,7 +761,7 @@ export function VideoControls({ onClick={getClickHandler(handlePlay, () => { void handleFullscreen() })} - onMouseMove={() => player?.userActive(true)} + onMouseMove={() => isPlayerReady(player) && player.userActive(true)} data-testid="VideoControls" > {/* Show title overlay only once on a single page */} @@ -780,7 +803,7 @@ export function VideoControls({ {images[0]?.mobileCinematicHigh != null && ( {imageAlt[0].value} void } +function isPlayerReady(player?: Player): player is Player { + return ( + player != null && + (typeof player.isDisposed !== 'function' || !player.isDisposed()) + ) +} + +function getPlayerCurrentTime(player?: Player): number { + return isPlayerReady(player) ? (player.currentTime() ?? 0) : 0 +} + +function getPlayerDuration(player?: Player): number { + return isPlayerReady(player) ? (player.duration() ?? 1) : 1 +} + +function getPlayerVolume(player?: Player): number { + return isPlayerReady(player) ? (player.volume() ?? 1) * 100 : 100 +} + function evtToDataLayer( eventType, mcId, @@ -130,7 +149,9 @@ export function VideoControls({ let retryTimeout: NodeJS.Timeout | undefined const updateDuration = (state: string): void => { - const playerDuration = player?.duration() + const playerDuration = isPlayerReady(player) + ? player.duration() + : undefined if ( playerDuration != null && @@ -196,7 +217,7 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), Math.round((progress / durationSeconds) * 100) ) const [, ...rest] = progressPercentNotYetEmitted @@ -218,10 +239,10 @@ export function VideoControls({ useEffect(() => { dispatchPlayer({ type: 'SetVolume', - volume: (player?.volume() ?? 1) * 100 + volume: getPlayerVolume(player) }) player?.on('play', () => { - if ((player?.currentTime() ?? 0) < 0.02) { + if (getPlayerCurrentTime(player) < 0.02) { eventToDataLayer( 'video_start', id, @@ -229,9 +250,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), Math.round( - ((player?.currentTime() ?? 0) / (player?.duration() ?? 1)) * 100 + (getPlayerCurrentTime(player) / getPlayerDuration(player)) * 100 ) ) } else { @@ -242,9 +263,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), Math.round( - ((player?.currentTime() ?? 0) / (player?.duration() ?? 1)) * 100 + (getPlayerCurrentTime(player) / getPlayerDuration(player)) * 100 ) ) } @@ -254,7 +275,7 @@ export function VideoControls({ }) }) player?.on('pause', () => { - if ((player?.currentTime() ?? 0) > 0.02) { + if (getPlayerCurrentTime(player) > 0.02) { eventToDataLayer( 'video_pause', id, @@ -262,9 +283,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), Math.round( - ((player?.currentTime() ?? 0) / (player?.duration() ?? 1)) * 100 + (getPlayerCurrentTime(player) / getPlayerDuration(player)) * 100 ) ) } @@ -276,29 +297,31 @@ export function VideoControls({ player?.on('timeupdate', () => { dispatchPlayer({ type: 'SetCurrentTime', - currentTime: secondsToTimeFormat(player?.currentTime() ?? 0, { + currentTime: secondsToTimeFormat(getPlayerCurrentTime(player), { trimZeroes: true }) }) dispatchPlayer({ type: 'SetProgress', - progress: Math.round(player?.currentTime() ?? 0) + progress: Math.round(getPlayerCurrentTime(player)) }) }) player?.on('volumechange', () => { dispatchPlayer({ type: 'SetMute', - mute: player?.muted() ?? false + mute: isPlayerReady(player) ? (player.muted() ?? false) : false }) dispatchPlayer({ type: 'SetVolume', - volume: (player?.volume() ?? 1) * 100 + volume: getPlayerVolume(player) }) }) player?.on('fullscreenchange', () => { dispatchPlayer({ type: 'SetFullscreen', - fullscreen: player?.isFullscreen() ?? false + fullscreen: isPlayerReady(player) + ? (player.isFullscreen() ?? false) + : false }) }) player?.on('useractive', () => @@ -334,9 +357,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), Math.round( - ((player?.currentTime() ?? 0) / (player?.duration() ?? 1)) * 100 + (getPlayerCurrentTime(player) / getPlayerDuration(player)) * 100 ) ) }) @@ -361,9 +384,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), Math.round( - ((player?.currentTime() ?? 0) / (player?.duration() ?? 1)) * 100 + (getPlayerCurrentTime(player) / getPlayerDuration(player)) * 100 ) ) } else { @@ -374,9 +397,9 @@ export function VideoControls({ title[0].value, variant?.language?.name.find(({ primary }) => !primary)?.value ?? variant?.language?.name[0]?.value, - Math.round(player?.currentTime() ?? 0), + Math.round(getPlayerCurrentTime(player)), Math.round( - ((player?.currentTime() ?? 0) / (player?.duration() ?? 1)) * 100 + (getPlayerCurrentTime(player) / getPlayerDuration(player)) * 100 ) ) } @@ -389,9 +412,9 @@ export function VideoControls({ function handlePlay(): void { if (!play) { - void player?.play() + if (isPlayerReady(player)) void player.play() } else { - void player?.pause() + if (isPlayerReady(player)) void player.pause() } } @@ -404,7 +427,7 @@ export function VideoControls({ }) } else { if (isMobile()) { - void player?.requestFullscreen() + if (isPlayerReady(player)) void player.requestFullscreen() dispatchPlayer({ type: 'SetFullscreen', fullscreen: true @@ -425,12 +448,12 @@ export function VideoControls({ type: 'SetProgress', progress: value }) - player?.currentTime(value) + if (isPlayerReady(player)) player.currentTime(value) } } function handleMute(): void { - player?.muted(!mute) + if (isPlayerReady(player)) player.muted(!mute) dispatchPlayer({ type: 'SetMute', mute: !mute @@ -444,7 +467,7 @@ export function VideoControls({ type: 'SetVolume', volume: value }) - player?.volume(value / 100) + if (isPlayerReady(player)) player.volume(value / 100) } } @@ -492,7 +515,7 @@ export function VideoControls({ onClick={getClickHandler(handlePlay, () => { void handleFullscreen() })} - onMouseMove={() => player?.userActive(true)} + onMouseMove={() => isPlayerReady(player) && player.userActive(true)} data-testid="VideoControls" > {!loading ? ( @@ -530,7 +553,7 @@ export function VideoControls({ {images[0]?.mobileCinematicHigh != null && (