diff --git a/packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt b/packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt index bd2804f8f3..27778022c3 100644 --- a/packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt +++ b/packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt @@ -48,6 +48,8 @@ class CallService : Service(), CallRepository.Listener { companion object { private const val TAG = "[Callingx] CallService" + internal const val DEFAULT_DISPLAY_NAME = "Unknown Caller" + internal const val EXTRA_CALL_ID = "extra_call_id" internal const val EXTRA_NAME = "extra_name" internal const val EXTRA_URI = "extra_uri" @@ -104,10 +106,12 @@ class CallService : Service(), CallRepository.Listener { ) return } + + val createdById = data["created_by_id"] + val createdName = data["created_by_display_name"].orEmpty() + val displayName = data["call_display_name"].orEmpty() + val callDisplayName = displayName.ifEmpty { createdName.ifEmpty { DEFAULT_DISPLAY_NAME } } - val callName = - data["call_display_name"].takeUnless { it.isNullOrBlank() } - ?: data["created_by_display_name"].orEmpty() val isVideo = data["video"] == "true" CallRegistrationStore.trackCallRegistration(callCid, null) @@ -116,8 +120,8 @@ class CallService : Service(), CallRepository.Listener { Intent(context, CallService::class.java).apply { action = ACTION_INCOMING_CALL putExtra(EXTRA_CALL_ID, callCid) - putExtra(EXTRA_URI, callCid.toUri()) - putExtra(EXTRA_NAME, callName) + putExtra(EXTRA_URI, createdById?.toUri() ?: callDisplayName.toUri()) + putExtra(EXTRA_NAME, callDisplayName) putExtra(EXTRA_IS_VIDEO, isVideo) } diff --git a/packages/react-native-sdk/ios/StreamVideoReactNative.m b/packages/react-native-sdk/ios/StreamVideoReactNative.m index fd55c24f43..848cee9071 100644 --- a/packages/react-native-sdk/ios/StreamVideoReactNative.m +++ b/packages/react-native-sdk/ios/StreamVideoReactNative.m @@ -25,6 +25,8 @@ NSNotificationName const kBroadcastStartedNotification = @"iOS_BroadcastStarted"; NSNotificationName const kBroadcastStoppedNotification = @"iOS_BroadcastStopped"; +static NSString *const DEFAULT_DISPLAY_NAME = @"Unknown Caller"; + static dispatch_queue_t _dictionaryQueue = nil; void broadcastNotificationCallback(CFNotificationCenterRef center, @@ -165,13 +167,10 @@ +(void)didReceiveIncomingPush:(PKPushPayload *)payload forType:(NSString *)type return; } - NSString *callDisplayName = streamPayload[@"call_display_name"]; - NSString *createdByDisplayName = streamPayload[@"created_by_display_name"]; - NSString *createdCallerName = callDisplayName.length > 0 ? callDisplayName : createdByDisplayName; NSString *callCid = streamPayload[@"call_cid"]; - if (!createdCallerName || !callCid) { + if (!callCid) { #if DEBUG - NSLog(@"[StreamVideoReactNative][didReceiveIncomingPush] Missing required fields: created_by_display_name or call_cid"); + NSLog(@"[StreamVideoReactNative][didReceiveIncomingPush] Missing required field: call_cid"); #endif if (completion) { completion(); @@ -209,6 +208,9 @@ +(void)reportNewIncomingCall:(NSDictionary *)streamPayload forType:(NSString *)t NSString *callDisplayName = streamPayload[@"call_display_name"]; NSString *createdByDisplayName = streamPayload[@"created_by_display_name"]; NSString *createdCallerName = callDisplayName.length > 0 ? callDisplayName : createdByDisplayName; + NSString *localizedCallerName = createdCallerName.length > 0 ? createdCallerName : DEFAULT_DISPLAY_NAME; + NSString *createdById = streamPayload[@"created_by_id"]; + NSString *handle = createdById.length > 0 ? createdById : localizedCallerName; NSString *videoIncluded = streamPayload[@"video"]; BOOL hasVideo = [videoIncluded isEqualToString:@"false"] ? NO : YES; NSString *handleType = @"generic"; @@ -223,10 +225,10 @@ +(void)reportNewIncomingCall:(NSDictionary *)streamPayload forType:(NSString *)t [invocation setTarget:callingxClass]; [invocation setSelector:selector]; [invocation setArgument:&callCid atIndex:2]; - [invocation setArgument:&createdCallerName atIndex:3]; + [invocation setArgument:&handle atIndex:3]; [invocation setArgument:&handleType atIndex:4]; [invocation setArgument:&hasVideo atIndex:5]; - [invocation setArgument:&createdCallerName atIndex:6]; + [invocation setArgument:&localizedCallerName atIndex:6]; [invocation setArgument:&supportsHolding atIndex:7]; [invocation setArgument:&supportsDTMF atIndex:8]; [invocation setArgument:&supportsGrouping atIndex:9]; diff --git a/packages/react-native-sdk/src/hooks/push/useCallingExpWithCallingStateEffect.ts b/packages/react-native-sdk/src/hooks/push/useCallingExpWithCallingStateEffect.ts index d14ec5565c..2229e7baef 100644 --- a/packages/react-native-sdk/src/hooks/push/useCallingExpWithCallingStateEffect.ts +++ b/packages/react-native-sdk/src/hooks/push/useCallingExpWithCallingStateEffect.ts @@ -20,13 +20,17 @@ export const useCallingExpWithCallingStateEffect = () => { const participants = useParticipants(); const activeCallCid = activeCall?.cid; + const createdByUserId = activeCall?.state.createdBy?.id; + const callCustomDisplayName = activeCall?.state.custom?.display_name; const currentUserId = activeCall?.currentUserId; const isIncoming = (activeCall?.ringing && !activeCall?.isCreatedByMe) || false; const callDisplayName = useMemo( - () => getCallDisplayName(callMembers, participants, currentUserId), - [callMembers, participants, currentUserId], + () => + callCustomDisplayName ?? + getCallDisplayName(callMembers, participants, currentUserId), + [callMembers, participants, currentUserId, callCustomDisplayName], ); useEffect(() => { @@ -108,11 +112,11 @@ export const useCallingExpWithCallingStateEffect = () => { callingx.updateDisplay( activeCallCid, - activeCallCid, + createdByUserId ?? callDisplayName, callDisplayName, isIncoming, ); - }, [activeCallCid, callDisplayName, isIncoming]); + }, [activeCallCid, createdByUserId, callDisplayName, isIncoming]); // Sync microphone mute state from app → CallKit useEffect(() => { diff --git a/packages/react-native-sdk/src/utils/internal/callingx/callingx.ts b/packages/react-native-sdk/src/utils/internal/callingx/callingx.ts index 146ed6b691..37b812d0c8 100644 --- a/packages/react-native-sdk/src/utils/internal/callingx/callingx.ts +++ b/packages/react-native-sdk/src/utils/internal/callingx/callingx.ts @@ -54,10 +54,13 @@ export function getCallDisplayName( } function getCallDisplayNameFromCall(call: Call): string { - return getCallDisplayName( - call.state.members, - call.state.participants, - call.currentUserId, + return ( + call.state.custom?.display_name ?? + getCallDisplayName( + call.state.members, + call.state.participants, + call.currentUserId, + ) ); } @@ -77,7 +80,7 @@ export async function registerOutgoingCall(call: Call) { logger.debug(`registerOutgoingCall: Registering outgoing call ${call.cid}`); await CallingxModule.startCall( call.cid, // unique id for call - call.id, // phone number for display in dialer (we use call id as phone number) + call.state.createdBy?.id ?? getCallDisplayNameFromCall(call), // handle for native call UI (prefer createdBy user id, fallback to call display name) getCallDisplayNameFromCall(call), // display name for display in call screen call.state.settings?.video?.enabled ?? false, // is video call? ); @@ -110,11 +113,11 @@ export async function joinCallingxCall(call: Call, activeCalls: Call[]) { isOutcomingCall || (!call.ringing && CallingxModule.isOngoingCallsEnabled) ) { - logger.debug(`joinCallingxCall: Joining call ${call.cid}`); try { + logger.debug(`joinCallingxCall: Joining call ${call.cid}`); await CallingxModule.startCall( call.cid, // unique id for call - call.id, // phone number for display in dialer (we use call id as phone number) + call.state.createdBy?.id ?? getCallDisplayNameFromCall(call), // handle for native call UI (prefer createdBy user id, fallback to call display name) getCallDisplayNameFromCall(call), // display name for display in call screen call.state.settings?.video?.enabled ?? false, // is video call? ); @@ -153,7 +156,7 @@ export async function joinCallingxCall(call: Call, activeCalls: Call[]) { // iOS early-returns with no error, Android sends the registered broadcast. await CallingxModule.displayIncomingCall( call.cid, // unique id for call - call.id, // phone number for display in dialer (we use call id as phone number) + call.state.createdBy?.id ?? getCallDisplayNameFromCall(call), // handle for native call UI (prefer createdBy user id, fallback to call display name) getCallDisplayNameFromCall(call), // display name for display in call screen call.state.settings?.video?.enabled ?? false, // is video call? );