diff --git a/src/components/surround.ts b/src/components/surround.ts index 4ff26aa8..45ee50f1 100644 --- a/src/components/surround.ts +++ b/src/components/surround.ts @@ -138,6 +138,7 @@ export class FrigateCardSurround extends LitElement { ...(media.getCameraID() && { camera: media.getCameraID() }), }) .removeContext('timeline') + .removeContext('mediaViewer') // Send the view change from the source of the tap event, so // the view change will be caught by the handler above (to // close the drawer). diff --git a/src/utils/media.ts b/src/utils/media.ts index 6bf63a93..f63f07a9 100644 --- a/src/utils/media.ts +++ b/src/utils/media.ts @@ -44,6 +44,17 @@ export const hideMediaControlsTemporarily = ( setControlsOnVideo(video, false); video._controlsHideTimer ??= new Timer(); video._controlsOriginalValue = oldValue; + + // LitElement may change the src attribute of the video element during + // rendering, so we need to ensure that the controls are reset on the 'old' + // video. See: + // https://github.com/dermotduffy/frigate-hass-card/issues/1310 + const resetIfReloaded = () => { + setControlsOnVideo(video, oldValue); + video.removeEventListener('loadstart', resetIfReloaded); + }; + video.addEventListener('loadstart', resetIfReloaded); + video._controlsHideTimer.start(seconds, () => { setControlsOnVideo(video, oldValue); }); diff --git a/tests/utils/media.test.ts b/tests/utils/media.test.ts index 01859e85..adbdd322 100644 --- a/tests/utils/media.test.ts +++ b/tests/utils/media.test.ts @@ -51,6 +51,24 @@ describe('hideMediaControlsTemporarily', () => { expect(video.controls).toBeTruthy(); expect(video._controlsHideTimer).toBeFalsy(); }); + + it('should add event listener that resets controls', () => { + const video: FrigateCardHTMLVideoElement = document.createElement('video'); + video.controls = true; + + hideMediaControlsTemporarily(video); + expect(video.controls).toBeFalsy(); + + // After a new media starts to load, the controls should reset. + video.dispatchEvent(new Event('loadstart')); + expect(video.controls).toBeTruthy(); + + // ... but only once, future loadstart events without subsequent calls to + // hideMediaControlsTemporarily() should do nothing. + video.controls = false; + video.dispatchEvent(new Event('loadstart')); + expect(video._controlsHideTimer).toBeFalsy(); + }); }); class NotAllowedError extends Error {