-
Notifications
You must be signed in to change notification settings - Fork 40
feat: callkit/telecom integration #2028
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…lkit-telecom-integration # Conflicts: # sample-apps/react-native/dogfood/ios/Podfile.lock # yarn.lock
…/GetStream/stream-video-js into feat/callkit-telecom-integration
# Conflicts: # yarn.lock
|
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 📝 WalkthroughWalkthroughIntroduces a comprehensive cross-platform calling library ( Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~100 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
sample-apps/react-native/expo-video-sample/app/index.tsx (1)
68-71: Remove dead code.This empty
useEffectserves no purpose and should be removed.🧹 Proposed fix
- useEffect(() => { - if (calls.length === 1) { - } - }, [calls]); -packages/react-native-sdk/expo-config-plugin/src/withAppDelegate.ts (2)
362-406: ObjC incoming push handler: missing nil/type checks forstreamandcidleading to potential crash.
If the push payload lacks or malforms thestreamdictionary orcall_cidfield,cidbecomesnil. PassingniltoaddCompletionHandlerand downstream methods causes a crash before the completion handler is invoked, violating the 30-second VoIP push completion requirement and triggering app termination by the system.Add validation to confirm
streamis a validNSDictionaryandcidis a valid non-emptyNSStringbefore proceeding. Callcompletion()and return early if validation fails.Proposed hardening
function addDidReceiveIncomingPushCallbackObjc(contents: string) { const onIncomingPush = ` // process the payload and store it in the native module's cache NSDictionary *stream = payload.dictionaryPayload[@"stream"]; NSString *cid = stream[@"call_cid"]; + + if (![stream isKindOfClass:[NSDictionary class]] || + ![cid isKindOfClass:[NSString class]] || + cid.length == 0) { + completion(); + return; + } // Check if user is busy BEFORE registering the call BOOL canProceed = [StreamVideoReactNative rejectIncomingCallIfNeeded:completion]; if (!canProceed) { return; } [RNVoipPushNotificationManager addCompletionHandler:cid completionHandler:completion];
307-360: Remove unnecessarycreated_by_display_namerequirement from Swift payload parsing; align with Objective-C implementation.The Swift guard at line 310 requires
stream["created_by_display_name"]but the Objective-C variant in the same function does not. The native implementation extracts this field as optional and uses it as the caller name. Drop the requirement to match the Objective-C pattern and avoid dropping legitimate pushes if that field is absent or null.Regarding the completion lifecycle:
RNVoipPushNotificationManager.addCompletionHandler()andRNVoipPushNotificationManager.didReceiveIncomingPush()(called beforeStreamVideoReactNative.didReceiveIncomingPush()) handle the completion callback. Verify that these two calls properly complete the handler within the 30-second VoIP push window to prevent system termination.sample-apps/react-native/expo-video-sample/package.json (1)
15-15: Remove unused CallKeep config plugin dependency.The
@config-plugins/react-native-callkeepdependency is not referenced in the Expo plugins array (app.json) and has no imports in the codebase. This is a leftover from the migration to@stream-io/react-native-callingxand should be removed from package.json.sample-apps/react-native/ringing-tutorial/package.json (1)
16-16: Remove the stale@config-plugins/react-native-callkeepdependency.The package.json still declares
@config-plugins/react-native-callkeep(line 16), and it remains configured in app.json, but it is not referenced anywhere in the application code. The@stream-io/video-react-native-sdkhas CallKeep integration built-in and is already configured with"ringing": truein app.json, making the separate config plugin redundant. Remove this dependency from both package.json and the plugins array in app.json.packages/react-native-sdk/ios/StreamVideoReactNative.m (1)
395-407: Logging tag + active-call detection criteria should be revisited.
- Log prefix is
[RNCallKeep](Line 402) but this code lives inStreamVideoReactNative.hasConnectedonly catches connected calls; you may want “active-ish” (!hasEnded) depending on why this is used.
🤖 Fix all issues with AI agents
In
@packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt:
- Around line 54-63: The capability flags are being combined with bitwise AND
causing a zeroed flag when bits don't overlap; change the combination in the
init block from CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING and
CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING to use the bitwise OR operator so
the resulting capabilities include both flags, updating the expression used to
build `capabilities` before calling
`CallsManager(context.applicationContext).apply {
registerAppWithTelecom(capabilities) }`.
In
@packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/ResourceUtils.kt:
- Around line 36-57: The bug is that getSoundUri builds the android.resource://
URI using the original sound string while getResourceIdByName uses a normalized
name for lookup, so names like "My-Sound.mp3" can resolve to a resourceId but
produce an incorrect URI; fix getSoundUri by computing and reusing the same
normalized resource name used for getResourceIdByName (e.g., strip extension via
substringBeforeLast and any normalization used by getResourceIdByName) into a
variable (e.g., resourceName) when soundResourceId is found, and construct the
URI as "android.resource://${context.packageName}/raw/$resourceName" instead of
using the original sound.
In @packages/react-native-callingx/CODE_OF_CONDUCT.md:
- Around line 59-67: Replace the placeholder "[INSERT CONTACT METHOD]" under the
"Enforcement" section with the real enforcement contact (for example a dedicated
email like [email protected] or a link to a GitHub issue template or support
channel) so the Code of Conduct has an actionable reporting mechanism; ensure
the inserted contact appears in the same sentence after "reported to the
community leaders responsible for enforcement at" and keep the rest of the
paragraph unchanged.
In @packages/react-native-callingx/ios/AudioSessionManager.swift:
- Around line 28-30: The assignment to mode uses AVAudioSession.Mode(rawValue:
modeString) which is failable and currently being force-unwrapped into a
non-optional mode; change the logic in the AudioSessionManager to safely unwrap
and validate the result from AVAudioSession.Mode(rawValue:) (using if let or
guard let) and only assign to the non-optional mode variable when the conversion
succeeds, otherwise keep a sensible default or handle the invalid input path
(e.g., log the bad modeString via audioSessionSettings and skip assignment) so
invalid mode strings cannot cause a runtime crash.
In @packages/react-native-sdk/ios/StreamVideoReactNative.m:
- Around line 99-150: The method didReceiveIncomingPush:completionHandler: must
always invoke the provided completion block; update each early-return path (when
streamPayload is nil, when callingxClass is nil, and when callingxClass does not
respond to selector) to call the incoming completion() before returning, and
change the local void (^completionHandler)(void) to forward the incoming
completion (use the completion parameter) when setting the argument for Callingx
invocation; also ensure that after invoking the selector on callingxClass you
still call completion() if Callingx does not itself invoke the block so the
system completion is always executed exactly once.
In @packages/react-native-sdk/src/utils/push/android.ts:
- Around line 322-372: The permission check block after calling
getNotifeeLibThrowIfNotInstalledForPush() logs when settings.authorizationStatus
!== 1 but fails to exit, causing the subsequent call notification flow
(createChannel, displayNotification, pushNonRingingCallData$.next) to run
erroneously; add an early return immediately after the logger.debug that says
"Notification permission not granted, unable to post ${data.type} notifications"
to stop execution when notifications are not authorized (i.e., keep the existing
log and then return from the enclosing function before the callChannel
handling).
🟠 Major comments (25)
sample-apps/react-native/dogfood/tsconfig.json-5-5 (1)
5-5: Remove "dom" from lib in React Native app.This React Native sample app has no
react-native-webdependency and targets only iOS and Android. Including"dom"in the TypeScript lib option allows DOM-specific APIs (document, window, HTMLElement) that don't exist in the React Native runtime, degrading type safety and potentially masking errors.Remove "dom" and keep only "esnext":
Suggested change
- "lib": ["esnext", "dom"], + "lib": ["esnext"],The base config
@react-native/typescript-configprovides appropriate types for React Native.packages/react-native-sdk/src/utils/push/setupIosVoipPushEvents.ts-26-34 (1)
26-34: Missing teardown fordidLoadWithEventslistener causes resource leak on logout.Line 26 adds a
addEventListener('didLoadWithEvents', ...)listener, but the cleanup callback at line 38 only removes the'notification'listener (line 42). WhenonPushLogout()is called, the'didLoadWithEvents'listener persists, creating a resource leak. The pattern inuseIosVoipPushEventsSetupEffect.tsshows the correct approach: both listeners must be removed during cleanup.Extract both handlers to named functions so they can be properly removed:
Proposed fix
- voipPushNotification.addEventListener('didLoadWithEvents', (events) => { + const onDidLoadWithEvents = (events: any[]) => { //we need this for cold start scenario where the app is not running and the events are not processed when the app is launched for (const event of events) { const { name, data } = event; if (name === 'RNVoipPushRemoteNotificationReceivedEvent') { onVoipNotificationReceived(data, pushConfig); } } - }); - voipPushNotification.addEventListener('notification', (notification) => { + }; + const onNotification = (notification: any) => { onVoipNotificationReceived(notification, pushConfig); - }); + }; + + voipPushNotification.addEventListener('didLoadWithEvents', onDidLoadWithEvents); + voipPushNotification.addEventListener('notification', onNotification); setPushLogoutCallback(async () => { videoLoggerSystem .getLogger('setPushLogoutCallback') .debug('notification event listener removed'); voipPushNotification.removeEventListener('notification'); + voipPushNotification.removeEventListener('didLoadWithEvents'); });packages/react-native-callingx/android/build.gradle-49-51 (1)
49-51: Replace deprecatedlintOptionswithlintblock.The
lintOptionsblock has been deprecated since Android Gradle Plugin 7.0. Modern Gradle versions use thelintblock instead.♻️ Proposed fix to use modern lint configuration
- lintOptions { + lint { disable "GradleCompatible" }packages/react-native-callingx/android/build.gradle-53-56 (1)
53-56: Update Java compatibility to version 11 or higher.Java 1.8 compatibility is outdated for modern React Native projects. React Native 0.73+ requires Java 11 as the minimum version. Consider updating to Java 11 or 17 for better compatibility and performance.
♻️ Proposed fix to use Java 11
compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 }packages/react-native-callingx/android/gradle.properties-1-1 (1)
1-1: Resolve Android Gradle Plugin 8.7.2 and Kotlin 2.0.21 version mismatch.Kotlin 2.0.21 is a valid stable release, but it is not officially compatible with AGP 8.7.2. The Kotlin compatibility matrix shows that KGP 2.0.21 supports only AGP up to 8.5. Either upgrade Kotlin to 2.1.20+ or 2.2.x (which support AGP 8.7.2), or downgrade AGP to 8.5 or lower to align with Kotlin 2.0.21.
packages/react-native-callingx/README.md-77-78 (1)
77-78: Complete the Android Setup section.The Android Setup section is empty, which is a critical documentation gap. Based on the PR changes introducing CallService, notification channels, and intent handling, users will need detailed guidance on:
- Required MainActivity modifications for handling call intents
- Notification channel configuration
- Permissions handling (especially POST_NOTIFICATIONS for Android 13+ and USE_FULL_SCREEN_INTENT for Android 11+)
- Potential Telecom API setup requirements
Would you like me to draft the Android Setup section based on the implementation details in the PR?
packages/react-native-callingx/ios/AudioSessionManager.swift-19-19 (1)
19-19: Reconsider.allowBluetoothA2DPfor VoIP calls.The default category options include
.allowBluetoothA2DP, which is typically used for high-quality music playback but introduces higher latency. For VoIP calls, the HFP (Hands-Free Profile) Bluetooth mode is generally preferred for lower latency. Consider whether A2DP should be included by default or made configurable based on use case.Based on learnings: iOS CallKit integration requires careful audio session configuration for call scenarios.
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/repo/LegacyCallRepository.kt-25-29 (1)
25-29: Add error handling to coroutine launches.Both coroutine launches (lines 28 and 77) lack error handling. If an exception occurs during collection, the coroutine will terminate silently, causing:
- Listener to stop receiving call state updates (line 28)
- Actions to stop being processed (line 77)
This could leave the call in an inconsistent state without any indication to the caller.
🛡️ Recommended error handling
override fun setListener(listener: Listener?) { this._listener = listener // Observe call state changes - scope.launch { currentCall.collect { _listener?.onCallStateChanged(it) } } + scope.launch { + try { + currentCall.collect { _listener?.onCallStateChanged(it) } + } catch (e: Exception) { + Log.e(TAG, "[repository] Error collecting call state", e) + } + } }// Process actions without telecom SDK scope.launch { - actionSource.consumeAsFlow().collect { action -> processActionLegacy(action) } + try { + actionSource.consumeAsFlow().collect { action -> processActionLegacy(action) } + } catch (e: Exception) { + Log.e(TAG, "[repository] Error processing call actions", e) + } }Also applies to: 76-79
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/model/Call.kt-34-47 (1)
34-47: Potential action loss with unbuffered channel and trySend.The
actionSourcechannel uses the default capacity (rendezvous/0), andprocessAction()usestrySend(), which fails immediately if no receiver is ready. Critical actions likeAnswerorDisconnectcould be silently dropped if the consumer is temporarily busy or the channel is closed.Consider one of the following solutions:
- Use a buffered channel (e.g.,
Channel<CallAction>(Channel.BUFFERED)) to queue actions- Document that callers must check the return value of
processAction()and retry on failure- Use
send()with suspension for guaranteed delivery (requires suspending caller)🔧 Recommended fix: Use buffered channel
data class Registered( val id: String, val callAttributes: CallAttributesCompat, val displayOptions: Bundle?, val isActive: Boolean, val isOnHold: Boolean, val isMuted: Boolean, val errorCode: Int?, val currentCallEndpoint: CallEndpointCompat?, val availableCallEndpoints: List<CallEndpointCompat>, - internal val actionSource: Channel<CallAction>, + internal val actionSource: Channel<CallAction> = Channel(Channel.BUFFERED), ) : Call() {Alternatively, update the channel creation at the call site in repositories to specify capacity.
packages/react-native-sdk/package.json-68-68 (1)
68-68: Consider tightening the version constraint for the 0.x release.The version constraint
>=0.1.0permits any future version including breaking changes in 0.x releases. For a newly introduced library still at 0.x, consider using a more restrictive constraint like^0.1.0or~0.1.0to prevent unexpected breaking changes.📦 Suggested version constraint
- "@stream-io/react-native-callingx": ">=0.1.0", + "@stream-io/react-native-callingx": "^0.1.0",This allows patch and minor updates within 0.x but prevents jumping to 1.x without explicit upgrade.
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt-310-329 (1)
310-329: Unsafe cast may cause crash if state changes concurrently.Line 313 casts
_currentCall.valuetoCall.Registeredwithout a null-safe check. If the state changes between the action dispatch and execution, this will throw aClassCastException.🔒 Proposed fix using safe cast
private suspend fun CallControlScope.doSwitchEndpoint(action: CallAction.SwitchAudioEndpoint) { Log.d(TAG, "[repository] doSwitchEndpoint: Switching to endpoint: ${action.endpointId}") - // TODO once availableCallEndpoints is a state flow we can just get the value - val endpoints = (_currentCall.value as Call.Registered).availableCallEndpoints + val call = _currentCall.value as? Call.Registered + if (call == null) { + Log.w(TAG, "[repository] doSwitchEndpoint: No registered call, ignoring") + return + } + val endpoints = call.availableCallEndpoints // Switch to the given endpoint or fallback to the best possible one. val newEndpoint = endpoints.firstOrNull { it.identifier == action.endpointId }packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt-51-52 (1)
51-52: Race condition onisSelfAnsweredandisSelfDisconnectedflags.These flags are read and written from multiple coroutines without synchronization. While the mutex protects call registration, these flags are accessed outside the mutex scope (e.g., in callbacks). Consider using
AtomicBooleanor encapsulating the flag check-and-reset in a synchronized block.🔒 Proposed fix using AtomicBoolean
+import java.util.concurrent.atomic.AtomicBoolean + @RequiresApi(Build.VERSION_CODES.O) class TelecomCallRepository(context: Context) : CallRepository(context) { companion object { private const val TAG = "[Callingx] TelecomCallRepository" } private var observeCallStateJob: Job? = null private val callsManager: CallsManager - private var isSelfAnswered = false - private var isSelfDisconnected = false + private val isSelfAnswered = AtomicBoolean(false) + private val isSelfDisconnected = AtomicBoolean(false)Then update usages to use
getAndSet(false)for atomic check-and-reset:val source = if (isSelfAnswered.getAndSet(false)) EventSource.APP else EventSource.SYSCommittable suggestion skipped: line range outside the PR's diff.
packages/react-native-callingx/src/EventManager.ts-15-53 (1)
15-53: Fix ref-counting:listenersCountcan desync (negative or unsubscribe while listeners still exist).
removeListener()decrements even if the callback wasn’t present (or was removed twice), and you never account for duplicates added. That can incorrectly callsubscription.remove()while listeners still exist.Minimal fix: decrement only if something was removed
removeListener<T extends EventName>( eventName: T, callback: EventListener<EventParams[T]>, ): void { const listeners = this.eventListeners.get(eventName) || []; - this.eventListeners.set( - eventName, - listeners.filter((c) => c !== callback), - ); - - this.listenersCount--; + const next = listeners.filter((c) => c !== callback); + this.eventListeners.set(eventName, next); + if (next.length !== listeners.length) { + this.listenersCount--; + } if (this.listenersCount === 0) { this.subscription?.remove(); this.subscription = null; } }Consider
Map<EventName, Set<EventListener<...>>>and derivelistenersCountfromsum(set.size)to make it impossible to desync.packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverActivity.kt-23-40 (1)
23-40: Don't broadcastCALL_ANSWERED_ACTIONwith a missingEXTRA_CALL_ID.
callIdcan be null (line 31) and you still send the broadcast (lines 33-39). If the receiver assumes a non-null call id, this becomes a crashy cold-start path.Proposed fix
private fun handleIntent(intent: Intent?) { if (intent == null) { return } //we need it only for answered call event, as for cold start case we need to send broadcast event and to launch the app if (intent.action == CallingxModule.CALL_ANSWERED_ACTION) { val callId = intent.getStringExtra(CallingxModule.EXTRA_CALL_ID) val source = intent.getStringExtra(CallingxModule.EXTRA_SOURCE) + if (callId.isNullOrEmpty()) return Intent(CallingxModule.CALL_ANSWERED_ACTION) .apply { setPackage(packageName) putExtra(CallingxModule.EXTRA_CALL_ID, callId) putExtra(CallingxModule.EXTRA_SOURCE, source) } .also { sendBroadcast(it) } } }packages/react-native-callingx/src/EventManager.ts-15-35 (1)
15-35: Gate the debug console.log behind__DEV__and add error handling for listener invocations.The
console.logat line 27 unconditionally logs every native event, including thecallIdidentifier. This causes production log spam and leaks sensitive user-session data. Additionally, if any listener throws an error, the forEach loop breaks and prevents remaining listeners from being notified.Proposed fix
if (this.subscription === null) { this.subscription = NativeCallingModule.onNewEvent((event: EventData) => { - console.log('[callingx] onNewEvent:', event); + if (__DEV__) { + // eslint-disable-next-line no-console + console.log('[callingx] onNewEvent:', event); + } const eventListeners = this.eventListeners.get(event.eventName as EventName) || []; - eventListeners.forEach((listener) => - listener(event.params as EventParams[EventName]), - ); + eventListeners.forEach((listener) => { + try { + listener(event.params as EventParams[EventName]); + } catch (e) { + if (__DEV__) { + // eslint-disable-next-line no-console + console.error('[callingx] listener error', e); + } + } + }); }); } }packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverService.kt-12-25 (1)
12-25: Wrap unhandledPendingIntent.send()calls inonCallAnswered()to prevent service crashes.Both
.send()calls (lines 31–36 and 38–44) can throwCanceledExceptionif the PendingIntent is no longer valid. Unhandled exceptions here will crash the service. Catch exceptions and log them to ensure the notification action fails gracefully.Proposed fix
private fun onCallAnswered(intent: Intent) { val callId = intent.getStringExtra(CallingxModule.EXTRA_CALL_ID) val source = intent.getStringExtra(CallingxModule.EXTRA_SOURCE) callId?.let { + try { NotificationIntentFactory.getPendingBroadcastIntent( applicationContext, CallingxModule.CALL_ANSWERED_ACTION, it ) { putExtra(CallingxModule.EXTRA_SOURCE, source) } .send() NotificationIntentFactory.getLaunchActivityIntent( applicationContext, CallingxModule.CALL_ANSWERED_ACTION, it, source ) .send() + } catch (t: Throwable) { + Log.w(TAG, "Failed to send call answered intent", t) + } } }Committable suggestion skipped: line range outside the PR's diff.
packages/react-native-callingx/src/utils/constants.ts-36-45 (1)
36-45: iOS end call reason mappings have semantic mismatches with CXCallEndedReason values.The constants in
iosEndCallReasonMapmap to incorrectCXCallEndedReasoncases based on their semantic meaning:
remote: 1maps to.failed, should be2(.remoteEnded)rejected: 4maps to.answeredElsewhere, should be5(.declinedElsewhere)answeredElsewhere: 3maps to.unanswered, should be4(.answeredElsewhere)Additionally,
error: 0andlocal: -1don't match any case in the switch statement (hit default and return without callingreportCall), which may silently fail to report call endings for these scenarios.packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt-108-148 (1)
108-148: Don’t throw fromonStartCommand; handle ACTION_STOP_SERVICE.Throwing on unknown action (Line 141-144) can crash the app from an external/malformed intent. Also
ACTION_STOP_SERVICEis defined (Line 58) but not handled.Proposed safer action handling
when (intent.action) { @@ ACTION_UPDATE_CALL -> { updateCall(intent) } + ACTION_STOP_SERVICE -> { + Log.d(TAG, "[service] onStartCommand: Stopping service") + stopSelf() + } else -> { - Log.e(TAG, "[service] onStartCommand: Unknown action: ${intent.action}") - throw IllegalArgumentException("Unknown action") + Log.e(TAG, "[service] onStartCommand: Unknown action: ${intent.action}; stopping") + stopSelf() + return START_NOT_STICKY } }Also applies to: 53-59
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/repo/CallRepository.kt-57-65 (1)
57-65:updateCall(...)ignores most of its parameters; likely breaks “update call UI” flows.
updateCallreceivescallId,displayName,address,isVideo, but only appliesdisplayOptions(Line 64). At minimum, validatecallIdmatches the current registered call and update relevant fields/attributes (or rename the API if this is intentionally “displayOptions-only”).Sketch: validate callId + apply more fields
open fun updateCall( callId: String, displayName: String, address: Uri, isVideo: Boolean, displayOptions: Bundle?, ) { - updateCurrentCall { copy(displayOptions = displayOptions) } + updateCurrentCall { + if (id != callId) return@updateCurrentCall this + copy( + callAttributes = createCallAttributes(displayName, address, isIncoming(), isVideo), + displayOptions = displayOptions, + ) + } }Also applies to: 71-94
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt-28-43 (1)
28-43: AvoidFLAG_MUTABLEunless strictly required (security + Play policy expectations).All these PendingIntents set action/extras up front, so they can typically be
FLAG_IMMUTABLE | FLAG_UPDATE_CURRENT. UsingFLAG_MUTABLE(Line 40, 62-63, 80) widens attack surface (intent tampering) without clear need.Proposed tightening
return PendingIntent.getService( context, REQUEST_CODE_SERVICE, intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) @@ return PendingIntent.getActivities( context, REQUEST_CODE_RECEIVER_ACTIVITY, arrayOf(launchActivityIntent, receiverIntent), - PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) @@ return PendingIntent.getActivity( context, REQUEST_CODE_LAUNCH_ACTIVITY, callIntent, - PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, )Also applies to: 44-64, 66-82
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt-332-345 (1)
332-345: Untrusted intent extras are force-unwrapped (!!) and can crash the process.
extractIntentParams()uses!!forEXTRA_CALL_ID,EXTRA_NAME, andEXTRA_URI(Line 333-340). Since intents can originate from notifications, broadcasts, or OEM flows, treat these as untrusted and fail gracefully.Safer parsing sketch
private fun extractIntentParams(intent: Intent): CallInfo { - val callId = intent.getStringExtra(EXTRA_CALL_ID)!! - val name = intent.getStringExtra(EXTRA_NAME)!! + val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: error("Missing EXTRA_CALL_ID") + val name = intent.getStringExtra(EXTRA_NAME) ?: "" @@ - val uri = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra(EXTRA_URI, Uri::class.java)!! - } else { - @Suppress("DEPRECATION") intent.getParcelableExtra(EXTRA_URI)!! - } + val uri: Uri = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getParcelableExtra(EXTRA_URI, Uri::class.java) + } else { + @Suppress("DEPRECATION") intent.getParcelableExtra(EXTRA_URI) + } ?: Uri.EMPTYAlso applies to: 321-330, 284-293
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt-52-63 (1)
52-63: HandlegetLaunchIntentForPackage(...) == nullto avoid broken notification taps.
getLaunchIntentForPackagecan return null (e.g., unusual manifests / disabled launcher activity). Today you constructIntent(launchIntent)anyway (Line 54, 69), which can degrade into an empty intent and make the PendingIntent effectively no-op.Also applies to: 67-75
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/HeadlessTaskManager.kt-18-25 (1)
18-25: Make classopenor fix the unsafe casting to prevent crashes.The class isn't declared
open(Line 18), so subclasses can't override theprotected open val reactNativeHostandprotected open val reactHostproperties (Lines 116–125). However, the KDoc at Lines 107–113 explicitly invites subclasses to override these properties, creating a contradiction.Additionally, the casts
(context.applicationContext as ReactApplication)on Lines 117 and 125 are unsafe and will crash withClassCastExceptionif the app's Application doesn't implementReactApplication.Either make the class
opento allow the documented override pattern, or replace the unsafe casts with null-checks or a factory pattern to handle apps that don't implementReactApplication.packages/react-native-callingx/package.json-2-14 (1)
2-14: Add missing CommonJS build target per repo's established pattern.This package only builds ESM and TypeScript definitions, but all other React Native packages in the repo (react-native-sdk, video-filters-react-native, noise-cancellation-react-native) use three targets:
["commonjs", "module", "typescript"]. Update the bob configuration to include the CommonJS target.Additionally:
- Replace placeholder description "test" with a descriptive summary.
- Update exports to use explicit
"import"and"require"conditions matching the build targets.- Consider adding
"react-native": "./src/index.tsx"entry point to aid Metro resolution.Suggested fix
{ "name": "@stream-io/react-native-callingx", "version": "0.1.0", - "description": "test", + "description": "Cross-platform calling (iOS CallKit + Android Telecom) for Stream Video React Native", "main": "./dist/module/index.js", + "react-native": "./src/index.tsx", "types": "./dist/typescript/src/index.d.ts", "exports": { ".": { "source": "./src/index.tsx", "types": "./dist/typescript/src/index.d.ts", + "import": "./dist/module/index.js", + "require": "./dist/commonjs/index.js", "default": "./dist/module/index.js" }, "./package.json": "./package.json" }, ... "react-native-builder-bob": { "source": "src", "output": "dist", "targets": [ + "commonjs", "module", "typescript" ] },packages/react-native-callingx/ios/CallingxImpl.swift-294-313 (1)
294-313: Duplicate event emission inonAudioRouteChange.The method emits the event twice: once directly via
eventEmitter?.emitEvent(dictionary)on line 311, and again viasendEvent()on line 312. This will cause listeners to receive duplicatedidChangeAudioRouteevents.Proposed fix - remove the duplicate emission
@objc private func onAudioRouteChange(_ notification: Notification) { guard let info = notification.userInfo, let reasonValue = info[AVAudioSessionRouteChangeReasonKey] as? UInt, let output = CallingxImpl.getAudioOutput() else { return } let params: [String: Any] = [ "output": output, "reason": reasonValue ] - let dictionary: [String: Any] = [ - "eventName": CallingxEvents.didChangeAudioRoute, - "params": params - ] - - eventEmitter?.emitEvent(dictionary) sendEvent(CallingxEvents.didChangeAudioRoute, body: params) }
🟡 Minor comments (26)
sample-apps/react-native/ringing-tutorial/utils/setFirebaseListeners.android.ts-28-32 (1)
28-32: Addawaitfor consistency and error handling.The foreground handler should await
onAndroidNotifeeEventjust like the background handler does on line 19. SinceonAndroidNotifeeEventis async, not awaiting it may result in unhandled Promise rejections if errors occur.⚡ Proposed fix
- notifee.onForegroundEvent((event) => { + notifee.onForegroundEvent(async (event) => { if (isNotifeeStreamVideoEvent(event)) { - onAndroidNotifeeEvent({ event }); + await onAndroidNotifeeEvent({ event }); } });sample-apps/react-native/expo-video-sample/app/index.tsx-9-9 (1)
9-9: Remove unused import.The
Viewimport is not used anywhere in this component.🧹 Proposed fix
- View,packages/react-native-sdk/src/hooks/push/useCallingExpWithCallingStateEffect.ts-171-173 (1)
171-173: Incorrect variable name in debug log.The log message references
isOutcomingCallbut the actual variable isisIncomingCall. This is misleading for debugging.🐛 Proposed fix
logger.debug( - `useEffect: ${activeCallCid} isCallRegistered: ${isCallRegistered} isOutcomingCall: ${isIncomingCall} prevState: ${prevState.current}, currentState: ${callingState} isOngoingCallsEnabled: ${callingx.isOngoingCallsEnabled}`, + `useEffect: ${activeCallCid} isCallRegistered: ${isCallRegistered} isIncomingCall: ${isIncomingCall} prevState: ${prevState.current}, currentState: ${callingState} isOngoingCallsEnabled: ${callingx.isOngoingCallsEnabled}`, );packages/react-native-sdk/src/hooks/push/useCallingExpWithCallingStateEffect.ts-258-273 (1)
258-273: Missing error handling forupdateDisplay.Other
callingxmethod calls (likeendCallWithReason,startCall,answerIncomingCall) include.catch()error handling, butupdateDisplaydoes not. This inconsistency could lead to silent failures.🐛 Proposed fix
- callingx.updateDisplay(activeCallCid, activeCallCid, callDisplayName); + callingx + .updateDisplay(activeCallCid, activeCallCid, callDisplayName) + .catch((error: unknown) => { + logger.error( + `Error updating display in calling exp: ${activeCallCid}`, + error, + ); + });packages/react-native-sdk/src/hooks/push/useCallingExpWithCallingStateEffect.ts-275-290 (1)
275-290: Missing error handling forsetMutedCall.Similar to
updateDisplay, this async call lacks error handling which is inconsistent with the pattern used elsewhere in this hook.🐛 Proposed fix
- callingx.setMutedCall(activeCallCid, isMute); + callingx.setMutedCall(activeCallCid, isMute).catch((error: unknown) => { + logger.error( + `Error setting mute state in calling exp: ${activeCallCid}`, + error, + ); + });packages/react-native-sdk/src/utils/push/setupCallingExpEvents.ts-90-107 (1)
90-107: Async error inonEndCallis silently lost.The
onEndCallhandler is async and callsprocessCallFromPushInBackground, but any rejection is unhandled since the event listener doesn't await the result. Per coding guidelines, native module calls should be wrapped in try-catch.Suggested error handling
const onEndCall = (pushConfig: PushConfig) => async ({ callId: call_cid, source }: EventParams['endCall']) => { videoLoggerSystem .getLogger('callingExpRejectCall') .debug( `callingExpRejectCall event callId: ${call_cid} source: ${source}`, ); if (source === 'app' || !call_cid) { //we only need to process the call if the call was rejected from the system return; } clearPushWSEventSubscriptions(call_cid); - await processCallFromPushInBackground(pushConfig, call_cid, 'decline'); + try { + await processCallFromPushInBackground(pushConfig, call_cid, 'decline'); + } catch (e) { + videoLoggerSystem + .getLogger('callingExpRejectCall') + .warn(`Failed to process decline for callId: ${call_cid}`, e); + } };packages/react-native-sdk/src/utils/push/internal/ios.ts-66-80 (1)
66-80: Native module calls lack error handling.Per coding guidelines, native module calls should be wrapped in try-catch to handle promise rejection from native code. The calls to
callingx.endCallWithReason(line 73) andvoipPushNotification.onVoipNotificationCompleted(line 76) could fail.Additionally,
client.onRingingCall(line 66) is not wrapped in error handling.Suggested error handling
- const callFromPush = await client.onRingingCall(call_cid); + let callFromPush; + try { + callFromPush = await client.onRingingCall(call_cid); + } catch (e) { + logger.error(`Failed to get ringing call for call_cid: ${call_cid}`, e); + return; + } function closeCallIfNecessary() { const mustEndCall = shouldCallBeClosed(callFromPush, notification?.stream); if (mustEndCall) { logger.debug(`callkeep.reportEndCallWithUUID for call_cid: ${call_cid}`); - //TODO: think about sending appropriate reason for end call - callingx.endCallWithReason(call_cid, 'local'); - - const voipPushNotification = getVoipPushNotificationLib(); - voipPushNotification.onVoipNotificationCompleted(call_cid); + try { + //TODO: think about sending appropriate reason for end call + callingx.endCallWithReason(call_cid, 'local'); + const voipPushNotification = getVoipPushNotificationLib(); + voipPushNotification.onVoipNotificationCompleted(call_cid); + } catch (e) { + logger.warn(`Failed to end call for call_cid: ${call_cid}`, e); + } return true; } return false; }packages/react-native-sdk/src/utils/StreamVideoRN/index.ts-126-137 (1)
126-137: Overly broad exception handling masks setup errors and sensitive data logging.The catch block catches any error and assumes it means the callingx library is missing. However,
extractCallingExpOptions()orcallingx.setup()could fail for other reasons (invalid config, runtime error), and the user would still see the misleading "library not installed" message. Additionally, logging the entirepushConfigobject exposes callbacks and internal configuration that should not be logged.Suggested fix
static setPushConfig(pushConfig: NonNullable<StreamVideoConfig['push']>) { try { const callingx = getCallingxLib(); videoLoggerSystem .getLogger('StreamVideoRN.setPushConfig') - .info(JSON.stringify({ pushConfig })); + .info('Setting push config'); const options = extractCallingExpOptions(pushConfig); callingx.setup(options); - } catch (_) { - throw new Error( - 'react-native-callingx library is not installed. Please check the installation instructions: https://getstream.io/video/docs/react-native/incoming-calls/ringing-setup/react-native/.', - ); + } catch (e) { + videoLoggerSystem + .getLogger('StreamVideoRN.setPushConfig') + .error('Failed to setup callingx', e); + throw e; }packages/react-native-callingx/android/build.gradle-12-12 (1)
12-12: Update Android Gradle Plugin to the latest stable version.AGP 8.7.2 is stable but outdated. The latest stable version is 8.13. Update to benefit from bug fixes, performance improvements, and compatibility enhancements with newer Android tooling.
sample-apps/react-native/dogfood/ios/AppDelegate.swift-72-77 (1)
72-77: Remove unusedcreated_by_display_nameextraction or document why validation is required.The
created_by_display_nameis extracted but immediately discarded with_. This enforces that the field must exist in the payload, but the value isn't used. If this field is no longer needed, remove it from the guard:guard let stream = payload.dictionaryPayload["stream"] as? [String: Any], - let _ = stream["created_by_display_name"] as? String, let cid = stream["call_cid"] as? String else {If the presence check is intentionally required for validation purposes, add a comment explaining why.
sample-apps/react-native/dogfood/ios/AppDelegate.swift-84-90 (1)
84-90: Clarify which component invokes the VoIP completion handler.The completion handler is passed to both
RNVoipPushNotificationManager.addCompletionHandler()andStreamVideoReactNative.didReceiveIncomingPush()in AppDelegate. However, the JS-side handler (onVoipNotificationReceived) does not invoke the completion directly—onlyvoipPushNotification.onVoipNotificationCompleted()is called in specific scenarios. Callingx (viaStreamVideoReactNative) ultimately handles completion invocation throughreportNewIncomingCall:...withCompletionHandler:.The current pattern works but is unclear. The expo-config-plugin template comments suggest "the JS SDK will handle the rest and call the completionHandler," which is misleading since it doesn't. Document which component is responsible for calling the completion handler to prevent confusion and potential regressions.
packages/react-native-callingx/.nvmrc-1-1 (1)
1-1: Align Node.js version with root .nvmrc (v24).v22.20.0 is a valid Node.js release, but it diverges from the root .nvmrc which specifies v24. Ensure consistency across the project's Node version specifications.
sample-apps/react-native/expo-video-sample/components/NavigationHeader.tsx-2-10 (1)
2-10: Remove unusedPressableimport.
Pressableis imported on line 5 but is not used anywhere in the component. The component now usesTouchableOpacityinstead.Suggested fix
import { Alert, Image, - Pressable, StyleSheet, Text, TouchableOpacity, View, } from 'react-native';packages/react-native-callingx/README.md-380-380 (1)
380-380: Document thehandleCallingIntentmethod.The troubleshooting section references
handleCallingIntentbut this method is not documented in the API Reference section (lines 294-316). Users need to know:
- The method signature
- When and where to call it (onCreate and onNewIntent in MainActivity)
- What parameters it accepts
- Example implementation
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt-394-404 (1)
394-404: TOCTOU race:_currentCall.valueread twice.Lines 398-399 read
_currentCall.valuetwice in a non-atomic manner. Between theischeck and the cast, the value could change, causing a potentialClassCastExceptionor incorrectcallId.🐛 Proposed fix
- var callId: String? = null - if (_currentCall.value is Call.Registered) { - callId = (_currentCall.value as Call.Registered).id - } + val callId = (_currentCall.value as? Call.Registered)?.idpackages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverActivity.kt-11-21 (1)
11-21: CallsetIntent(intent)inonNewIntent, or remove reliance onActivity.intent.Right now you pass
intentdirectly, so it works, but callingsetIntent(intent)is the standard contract and avoids surprises if future code readsthis.intent.packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallingxModule.kt-367-400 (1)
367-400: Potential bug:paramsMapis created but not used in the emit call.At line 383,
paramsis emitted instead of the newly createdparamsMap. TheparamsMapvariable is built by iterating overparamsbut is only used in the map passed toemitOnNewEvent. This appears inconsistent.If the intent was to emit a normalized map, consider:
Suggested fix
reactApplicationContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - .emit(eventName, params) + .emit(eventName, paramsMap)packages/react-native-callingx/src/CallingxModule.ts-144-146 (1)
144-146: Fix void return type mismatch.The function has a
voidreturn type but includes areturnstatement. Per the static analysis hint, this should be fixed.Suggested fix
clearInitialEvents(): void { - return NativeCallingModule.clearInitialEvents(); + NativeCallingModule.clearInitialEvents(); }packages/react-native-callingx/ios/UUIDStorage.swift-15-33 (1)
15-33: Potential data corruption: fallback to new UUID on parse failure.At line 21, if
UUID(uuidString: existingUUID)fails (returns nil), a new randomUUID()is returned. This creates a mismatch between the stored string and the returned UUID, potentially corrupting the bidirectional mapping.Since the stored value was created from a valid UUID (line 26), parse failure should be impossible unless storage is corrupted. Consider asserting or logging an error instead.
Suggested fix
if let existingUUID = uuidDict[cid] { #if DEBUG print("[UUIDStorage] getUUIDForCid: found existing UUID \(existingUUID) for cid \(cid)") #endif - return UUID(uuidString: existingUUID) ?? UUID() + guard let uuid = UUID(uuidString: existingUUID) else { + assertionFailure("[UUIDStorage] Failed to parse stored UUID: \(existingUUID)") + // Fallback: recreate and update storage + let newUUID = UUID() + let uuidString = newUUID.uuidString.lowercased() + uuidDict[cid] = uuidString + cidDict[uuidString] = cid + return newUUID + } + return uuid }packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/HeadlessTaskManager.kt-44-53 (1)
44-53: ClearactiveTaskIdon stop to avoid “stuck active” if finish callback doesn’t fire.
stopHeadlessTask()finishes the task (Line 44-53, 78-83) but doesn’t clearactiveTaskIdunlessonHeadlessJsTaskFinishruns (Line 102-105). Clearing it when you explicitly stop is safer.Also applies to: 78-87, 102-105
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt-84-104 (1)
84-104: Use a requestCode that’s unique per (action, callId), not just action hashCode.
action.hashCode()(Line 100) can collide and will also intentionally reuse the same PendingIntent across different calls (if that ever happens), leading to stale extras even withFLAG_UPDATE_CURRENT.Safer requestCode
return PendingIntent.getBroadcast( context, - action.hashCode(), + (31 * action.hashCode()) + callId.hashCode(), intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT )packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/notifications/CallNotificationManager.kt-46-84 (1)
46-84: Full-screen intent is set for all call states; usually should be incoming-only.
setFullScreenIntent(contentIntent, true)(Line 62) on an ongoing call notification can cause unexpected UI popping depending on OEM behavior. Consider limiting it to “incoming + not active”.Suggested adjustment
val builder = NotificationCompat.Builder(context, channelId) .setContentIntent(contentIntent) - .setFullScreenIntent(contentIntent, true) .setStyle(callStyle) @@ +if (call.isIncoming() && !call.isActive) { + builder.setFullScreenIntent(contentIntent, true) +}Committable suggestion skipped: line range outside the PR's diff.
packages/react-native-callingx/package.json-58-71 (1)
58-71: NarrowpeerDependenciesto match minimum supported React Native version.
peerDependencieswith"*"(lines 68-71) allows any React Native version, including versions before 0.73.0. However, the README specifies "React Native 0.73+" as the minimum requirement for New Architecture/Turbo Modules support. Additionally, the Android code usescom.facebook.react.ReactHost, which was introduced in React Native 0.76+. UpdatepeerDependenciesto enforce the minimum supported version:"react-native": ">=0.76.0".packages/react-native-callingx/src/spec/NativeCallingx.ts-36-45 (1)
36-45: Inconsistent event params betweengetInitialEventsandonNewEvent.
getInitialEventsreturns params with asourcefield (Line 43), butonNewEventparams definition (Lines 104-109) doesn't includesource. This inconsistency could cause runtime issues when processing initial events vs. live events.Proposed fix - add `source` to onNewEvent params
readonly onNewEvent: EventEmitter<{ eventName: string; params: { callId: string; cause?: string; muted?: boolean; hold?: boolean; + source?: string; }; }>;Also applies to: 102-110
packages/react-native-callingx/src/spec/NativeCallingx.ts-62-63 (1)
62-63: Fix typo in comment."withing" should be "within".
Proposed fix
- //use when need to answer an incoming call withing app UI + //use when need to answer an incoming call within app UIpackages/react-native-callingx/src/types.ts-10-18 (1)
10-18: Malformed JSDoc comment syntax.There's a duplicate
/**opening on lines 10-11 which creates invalid JSDoc. The inner/**should just be*.Proposed fix
/** - /** - * Setup the module. This method must be called before any other method. - * For iOS, the module will setup CallKit parameters. - * See: {@link InternalIOSOptions} - * For Android, the module will create notification channels. - * See: {@link InternalAndroidOptions} - * @param options - The options to setup the callingx module. See {@link CallingExpOptions} - */ + * Setup the module. This method must be called before any other method. + * For iOS, the module will setup CallKit parameters. + * See: {@link InternalIOSOptions} + * For Android, the module will create notification channels. + * See: {@link InternalAndroidOptions} + * @param options - The options to setup the callingx module. See {@link CallingExpOptions} + */
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/CallService.kt
Show resolved
Hide resolved
...native-callingx/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt
Show resolved
Hide resolved
packages/react-native-callingx/android/src/main/java/io/getstream/rn/callingx/ResourceUtils.kt
Show resolved
Hide resolved
| ## Enforcement | ||
|
|
||
| Instances of abusive, harassing, or otherwise unacceptable behavior may be | ||
| reported to the community leaders responsible for enforcement at | ||
| [INSERT CONTACT METHOD]. | ||
| All complaints will be reviewed and investigated promptly and fairly. | ||
|
|
||
| All community leaders are obligated to respect the privacy and security of the | ||
| reporter of any incident. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fill in the enforcement contact method before release.
Line 63 contains a placeholder [INSERT CONTACT METHOD] that must be replaced with an actual contact mechanism (e.g., email address, GitHub issue form link, or support channel) for the Code of Conduct enforcement process to be functional.
📋 Replace the placeholder with the intended contact method
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
-[INSERT CONTACT METHOD].
+[email protected] <!-- or appropriate contact method -->📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## Enforcement | |
| Instances of abusive, harassing, or otherwise unacceptable behavior may be | |
| reported to the community leaders responsible for enforcement at | |
| [INSERT CONTACT METHOD]. | |
| All complaints will be reviewed and investigated promptly and fairly. | |
| All community leaders are obligated to respect the privacy and security of the | |
| reporter of any incident. | |
| ## Enforcement | |
| Instances of abusive, harassing, or otherwise unacceptable behavior may be | |
| reported to the community leaders responsible for enforcement at | |
| [email protected] <!-- or appropriate contact method --> | |
| All complaints will be reviewed and investigated promptly and fairly. | |
| All community leaders are obligated to respect the privacy and security of the | |
| reporter of any incident. |
🤖 Prompt for AI Agents
In @packages/react-native-callingx/CODE_OF_CONDUCT.md around lines 59 - 67,
Replace the placeholder "[INSERT CONTACT METHOD]" under the "Enforcement"
section with the real enforcement contact (for example a dedicated email like
[email protected] or a link to a GitHub issue template or support channel) so
the Code of Conduct has an actionable reporting mechanism; ensure the inserted
contact appears in the same sentence after "reported to the community leaders
responsible for enforcement at" and keep the rest of the paragraph unchanged.
…' into feat/callkit-telecom-integration
… notifee (#2082) ### 💡 Overview Previously we used Notifee for the foreground service to keep the call alive when app goes to background. Now this foreground service is added to our SDK. <img width="640" height="400" alt="image" src="https://github.com/user-attachments/assets/83750dd9-0602-47d5-835e-952d6d64277d" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an SDK-managed keep-alive service for Android calls, enabling better background call persistence with system notifications. * Added support for Android 13+ notification permissions, ensuring proper permission requests before displaying call notifications. * **Improvements** * Enhanced Android notification channel configuration for better call state management. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Artsiom Grintsevich <[email protected]> Co-authored-by: GitHub Actions Bot <> Co-authored-by: Oliver Lazoroski <[email protected]> Co-authored-by: jdimovska <[email protected]>
💡 Overview
📝 Implementation notes
🎫 Ticket: https://linear.app/stream/issue/RN-17/android-support-for-telecom-manager
📑 Docs: https://github.com/GetStream/docs-content/pull/881
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.