From 481b9a6e58a3ae0eb6b038c26c1f2064fd0f6936 Mon Sep 17 00:00:00 2001 From: Calinteodor Date: Mon, 19 Jan 2026 16:07:59 +0200 Subject: [PATCH] feat(tracks): add/remove "ended" listener when screen sharing * Listen to the MediaStreamTrack "ended" event so local screen-share state is toggled off when capture is terminated externally. --- react/features/base/tracks/actions.any.ts | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/react/features/base/tracks/actions.any.ts b/react/features/base/tracks/actions.any.ts index 89e3b1b97b..a1501c3132 100644 --- a/react/features/base/tracks/actions.any.ts +++ b/react/features/base/tracks/actions.any.ts @@ -29,6 +29,7 @@ import { TRACK_UPDATED, TRACK_WILL_CREATE } from './actionTypes'; +import { toggleScreensharing } from './actions'; import { createLocalTracksF, getCameraFacingMode, @@ -385,8 +386,22 @@ export function trackAdded(track: any) { const mediaType = track.getVideoType() === VIDEO_TYPE.DESKTOP ? MEDIA_TYPE.SCREENSHARE : track.getType(); + let isReceivingData, noDataFromSourceNotificationInfo, participantId; + // Make screen share toggle off listen to MediaStreamTrack "ended" event + // when it's terminated via Android status bar chip. + if (navigator.product === 'ReactNative') { + const mediaStreamTrack = track?.getTrack?.(); + + if (mediaType === MEDIA_TYPE.SCREENSHARE) { + const onEnded = () => dispatch(toggleScreensharing(false)); + + mediaStreamTrack.addEventListener('ended', onEnded); + track._onEnded = onEnded; + } + } + if (local) { // Reset the no data from src notification state when we change the track, as it's context is set // on a per device basis. @@ -568,6 +583,16 @@ export function trackRemoved(track: any): { track.removeAllListeners(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED); track.removeAllListeners(JitsiTrackEvents.NO_DATA_FROM_SOURCE); + // Remove MediaStreamTrack "ended" event. + if (navigator.product === 'ReactNative') { + const mediaStreamTrack = track?.getTrack?.(); + + if (track._onEnded) { + mediaStreamTrack.removeEventListener('ended', track._onEnded); + delete track._onEnded; + } + } + return { type: TRACK_REMOVED, track: {