@@ -71,6 +71,17 @@ export class CameraSource extends LitUploaderBlock {
7171 private _cameraDevices : CameraDeviceOption [ ] = [ ] ;
7272 private _audioDevices : AudioDeviceOption [ ] = [ ] ;
7373 private _permissionResponses : Partial < Record < ( typeof DEFAULT_PERMISSIONS ) [ number ] , PermissionStatus > > = { } ;
74+ private _permissionCleanupFns : Array < ( ) => void > = [ ] ;
75+
76+ private readonly _handlePreviewPlay = ( ) : void => {
77+ this . _startTimeline ( ) ;
78+ this . currentTimelineIcon = 'pause' ;
79+ } ;
80+
81+ private readonly _handlePreviewPause = ( ) : void => {
82+ this . currentTimelineIcon = 'play' ;
83+ this . _stopTimeline ( ) ;
84+ } ;
7485
7586 private timerRef = createRef < HTMLElement > ( ) ;
7687 private lineRef = createRef < HTMLElement > ( ) ;
@@ -416,21 +427,24 @@ export class CameraSource extends LitUploaderBlock {
416427 this . video = null ;
417428 videoElement . src = videoURL ;
418429
419- videoElement . addEventListener ( 'play' , ( ) => {
420- this . _startTimeline ( ) ;
421- this . currentTimelineIcon = 'pause' ;
422- } ) ;
423-
424- videoElement . addEventListener ( 'pause' , ( ) => {
425- this . currentTimelineIcon = 'play' ;
426- this . _stopTimeline ( ) ;
427- } ) ;
430+ this . _attachPreviewListeners ( videoElement ) ;
428431 } catch ( error ) {
429432 console . error ( 'Failed to preview video' , error ) ;
430433 this . telemetryManager . sendEventError ( error , 'camera previewing. Failed to preview video' ) ;
431434 }
432435 } ;
433436
437+ private _attachPreviewListeners ( videoElement : HTMLVideoElement ) : void {
438+ this . _detachPreviewListeners ( videoElement ) ;
439+ videoElement . addEventListener ( 'play' , this . _handlePreviewPlay ) ;
440+ videoElement . addEventListener ( 'pause' , this . _handlePreviewPause ) ;
441+ }
442+
443+ private _detachPreviewListeners ( videoElement ?: HTMLVideoElement | null ) : void {
444+ videoElement ?. removeEventListener ( 'play' , this . _handlePreviewPlay ) ;
445+ videoElement ?. removeEventListener ( 'pause' , this . _handlePreviewPause ) ;
446+ }
447+
434448 _retake = ( ) : void => {
435449 this . _setCameraState ( CameraSourceEvents . RETAKE ) ;
436450
@@ -723,6 +737,7 @@ export class CameraSource extends LitUploaderBlock {
723737 }
724738 const tracks = this . video ?. getTracks ?.( ) ;
725739 tracks ?. [ 0 ] ?. stop ( ) ;
740+ this . _detachPreviewListeners ( this . videoRef . value ) ;
726741 this . video = null ;
727742
728743 this . _makeStreamInactive ( ) ;
@@ -783,21 +798,40 @@ export class CameraSource extends LitUploaderBlock {
783798
784799 _permissionAccess = async ( ) : Promise < void > => {
785800 try {
801+ this . _teardownPermissionListeners ( ) ;
786802 for ( const permission of DEFAULT_PERMISSIONS ) {
787803 const response = await navigator . permissions . query ( {
788804 name : permission as PermissionName ,
789805 } ) ;
790806 this . _permissionResponses [ permission ] = response ;
791807
792808 response . addEventListener ( 'change' , this . _handlePermissionsChange ) ;
809+ this . _permissionCleanupFns . push ( ( ) => {
810+ response . removeEventListener ( 'change' , this . _handlePermissionsChange ) ;
811+ } ) ;
793812 }
813+ this . _unsubPermissions = ( ) => {
814+ this . _teardownPermissionListeners ( ) ;
815+ } ;
794816 } catch ( error ) {
817+ this . _teardownPermissionListeners ( ) ;
795818 console . log ( 'Failed to use permissions API. Fallback to manual request mode.' , error ) ;
796819 this . telemetryManager . sendEventError ( error , 'camera permissions. Failed to use permissions API' ) ;
797820 this . _capture ( ) ;
798821 }
799822 } ;
800823
824+ private _teardownPermissionListeners ( ) : void {
825+ if ( this . _permissionCleanupFns . length === 0 ) {
826+ return ;
827+ }
828+ for ( const cleanup of this . _permissionCleanupFns ) {
829+ cleanup ( ) ;
830+ }
831+ this . _permissionCleanupFns = [ ] ;
832+ this . _unsubPermissions = null ;
833+ }
834+
801835 _getPermission = ( ) : void => { } ;
802836
803837 _requestDeviceAccess = async ( ) : Promise < void > => {
@@ -909,11 +943,9 @@ export class CameraSource extends LitUploaderBlock {
909943 }
910944
911945 _destroy ( ) : void {
912- for ( const permission of DEFAULT_PERMISSIONS ) {
913- this . _permissionResponses [ permission ] ?. removeEventListener ( 'change' , this . _handlePermissionsChange ) ;
914- }
915-
946+ this . _teardownPermissionListeners ( ) ;
916947 navigator . mediaDevices ?. removeEventListener ( 'devicechange' , this . _getDevices ) ;
948+ this . _detachPreviewListeners ( this . videoRef . value ) ;
917949 }
918950
919951 override disconnectedCallback ( ) : void {
0 commit comments