11import RNCallKeep from 'react-native-callkeep' ;
22import { DeviceEventEmitter , NativeEventEmitter } from 'react-native' ;
33
4- import { isIOS } from '../../methods/helpers' ;
4+ import { isIOS , normalizeDeepLinkingServerHost } from '../../methods/helpers' ;
55import store from '../../store' ;
66import { deepLinkingOpen } from '../../../actions/deepLinking' ;
77import { useCallStore } from './useCallStore' ;
@@ -17,6 +17,15 @@ const TAG = `[MediaCallEvents][${platform}]`;
1717const EVENT_VOIP_ACCEPT_FAILED = 'VoipAcceptFailed' ;
1818const EVENT_VOIP_ACCEPT_SUCCEEDED = 'VoipAcceptSucceeded' ;
1919
20+ /** True when normalized incoming host matches the active Redux workspace (no server switch needed). */
21+ function isVoipIncomingHostCurrentWorkspace ( incomingHost : string ) : boolean {
22+ const active = store . getState ( ) . server . server ;
23+ if ( ! active || ! incomingHost ) {
24+ return false ;
25+ }
26+ return normalizeDeepLinkingServerHost ( incomingHost ) === normalizeDeepLinkingServerHost ( active ) ;
27+ }
28+
2029/** Dedupe native emit + stash replay for the same failed accept. */
2130let lastHandledVoipAcceptFailureCallId : string | null = null ;
2231/** Idempotent warm delivery of native accept success. */
@@ -56,6 +65,12 @@ function handleVoipAcceptSucceededFromNative(data: VoipPayload) {
5665 console . log ( `${ TAG } VoipAcceptSucceeded:` , data ) ;
5766 NativeVoipModule . clearInitialEvents ( ) ;
5867 useCallStore . getState ( ) . setNativeAcceptedCallId ( data . callId ) ;
68+ if ( data . host && isVoipIncomingHostCurrentWorkspace ( data . host ) ) {
69+ mediaSessionInstance . applyRestStateSignals ( ) . catch ( error => {
70+ console . error ( `${ TAG } applyRestStateSignals failed:` , error ) ;
71+ } ) ;
72+ return ;
73+ }
5974 store . dispatch (
6075 deepLinkingOpen ( {
6176 callId : data . callId ,
@@ -109,8 +124,8 @@ export const setupMediaCallEvents = (): (() => void) => {
109124
110125 // Note: there is intentionally no 'answerCall' listener here.
111126 // VoipService.swift handles accept natively: handleObservedCallChanged detects
112- // hasConnected = true and calls handleNativeAccept(), which sends the DDP accept
113- // signal before JS runs. JS receives VoipAcceptSucceeded after success.
127+ // hasConnected = true and calls handleNativeAccept(), which sends the REST accept
128+ // (POST /api/v1/media-calls.answer) before JS runs. JS receives VoipAcceptSucceeded after success.
114129 }
115130
116131 /** Tracks OS-driven hold (competing call) so we only auto-resume that path, not manual hold. */
@@ -223,6 +238,14 @@ export const getInitialMediaCallEvents = async (): Promise<boolean> => {
223238 if ( wasAnswered ) {
224239 useCallStore . getState ( ) . setNativeAcceptedCallId ( initialEvents . callId ) ;
225240
241+ if ( initialEvents . host && isVoipIncomingHostCurrentWorkspace ( initialEvents . host ) ) {
242+ mediaSessionInstance . applyRestStateSignals ( ) . catch ( error => {
243+ console . error ( `${ TAG } applyRestStateSignals (initial) failed:` , error ) ;
244+ } ) ;
245+ console . log ( `${ TAG } Same workspace as VoIP host; skipped deepLinkingOpen` ) ;
246+ return true ;
247+ }
248+
226249 store . dispatch (
227250 deepLinkingOpen ( {
228251 callId : initialEvents . callId ,
0 commit comments