Skip to content

Commit a42e478

Browse files
committed
Fix Android and iOS builds, signaling, and WebRTC calling
- Fix Android monorepo build: set root, entryFile, hermesCommand paths, add dotenv.gradle for env var injection, register bundle command via react-native.config.js (CLI v20 compat) - Add Android permissions (CAMERA, RECORD_AUDIO, MODIFY_AUDIO_SETTINGS), enable cleartext traffic and network security config for LAN dev - Fix iOS: enable ATS arbitrary loads for ws:// signaling, configure react-native-permissions setup_permissions in Podfile - Fix signaling auth to support both JWKS (ES256) and HS256 JWT verification for Supabase key rotation - Add debug message logging to signaling server - Fix ContactsScreen to call startCall() instead of navigating directly - Fix CallManager: listen for signaling on construction, persist listener across calls instead of unsubscribing in teardown - Fix WebRTCService: use addTrack() instead of removed addStream() - Wire up RTCView in ActiveCallScreen for live video rendering
1 parent d9294f6 commit a42e478

File tree

14 files changed

+213
-107
lines changed

14 files changed

+213
-107
lines changed

apps/mobile/android/app/build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
apply plugin: "com.android.application"
22
apply plugin: "org.jetbrains.kotlin.android"
33
apply plugin: "com.facebook.react"
4+
apply from: file("../../../../node_modules/react-native-config/android/dotenv.gradle")
45

56
/**
67
* This is the configuration block to customize your React Native Android app.
78
* By default you don't need to apply any configuration, just uncomment the lines you need.
89
*/
910
react {
1011
/* Folders — overridden for monorepo (npm workspaces hoists to root node_modules) */
11-
root = file("../../../../")
12+
root = file("../../")
1213
reactNativeDir = file("../../../../node_modules/react-native")
1314
codegenDir = file("../../../../node_modules/@react-native/codegen")
1415
cliFile = file("../../../../node_modules/react-native/cli.js")
@@ -33,15 +34,15 @@ react {
3334
// bundleAssetName = "MyApplication.android.bundle"
3435
//
3536
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
36-
// entryFile = file("../js/MyApplication.android.js")
37+
entryFile = file("../../index.js")
3738
//
3839
// A list of extra flags to pass to the 'bundle' commands.
3940
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
4041
// extraPackagerArgs = []
4142

4243
/* Hermes Commands */
4344
// The hermes compiler command to run. By default it is 'hermesc'
44-
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
45+
hermesCommand = file("../../../../node_modules/hermes-compiler/hermesc/osx-bin/hermesc").absolutePath
4546
//
4647
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
4748
// hermesFlags = ["-O", "-output-source-map"]

apps/mobile/android/app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
22

33
<uses-permission android:name="android.permission.INTERNET" />
4+
<uses-permission android:name="android.permission.CAMERA" />
5+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
6+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
7+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
48

59
<application
610
android:name=".MainApplication"
@@ -9,7 +13,8 @@
913
android:roundIcon="@mipmap/ic_launcher_round"
1014
android:allowBackup="false"
1115
android:theme="@style/AppTheme"
12-
android:usesCleartextTraffic="${usesCleartextTraffic}"
16+
android:usesCleartextTraffic="true"
17+
android:networkSecurityConfig="@xml/network_security_config"
1318
android:supportsRtl="true">
1419
<activity
1520
android:name=".MainActivity"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<network-security-config>
3+
<!-- Allow cleartext (ws://) to local network for development -->
4+
<domain-config cleartextTrafficPermitted="true">
5+
<domain includeSubdomains="true">192.168.68.51</domain>
6+
<domain includeSubdomains="true">10.0.0.0</domain>
7+
<domain includeSubdomains="true">localhost</domain>
8+
</domain-config>
9+
</network-security-config>

apps/mobile/ios/Farscry.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@
260260
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
261261
CLANG_ENABLE_MODULES = YES;
262262
CURRENT_PROJECT_VERSION = 1;
263+
DEVELOPMENT_TEAM = 8CXXACWV56;
263264
ENABLE_BITCODE = NO;
264265
INFOPLIST_FILE = Farscry/Info.plist;
265266
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
@@ -290,6 +291,7 @@
290291
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
291292
CLANG_ENABLE_MODULES = YES;
292293
CURRENT_PROJECT_VERSION = 1;
294+
DEVELOPMENT_TEAM = 8CXXACWV56;
293295
INFOPLIST_FILE = Farscry/Info.plist;
294296
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
295297
LD_RUNPATH_SEARCH_PATHS = (

apps/mobile/ios/Farscry/Info.plist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
<key>NSAppTransportSecurity</key>
3030
<dict>
3131
<key>NSAllowsArbitraryLoads</key>
32-
<false/>
33-
<key>NSAllowsLocalNetworking</key>
3432
<true/>
3533
</dict>
3634
<key>NSMicrophoneUsageDescription</key>

apps/mobile/ios/Podfile

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
# Resolve react_native_pods.rb with node to allow for hoisting
2-
require Pod::Executable.execute_command('node', ['-p',
3-
'require.resolve(
4-
"react-native/scripts/react_native_pods.rb",
5-
{paths: [process.argv[1]]},
6-
)', __dir__]).strip
1+
def node_require(script)
2+
require Pod::Executable.execute_command('node', ['-p',
3+
"require.resolve(
4+
'#{script}',
5+
{paths: [process.argv[1]]},
6+
)", __dir__]).strip
7+
end
8+
9+
node_require('react-native/scripts/react_native_pods.rb')
10+
node_require('react-native-permissions/scripts/setup.rb')
711

812
platform :ios, min_ios_version_supported
913
prepare_react_native_project!
1014

15+
setup_permissions([
16+
'Camera',
17+
'Microphone',
18+
'Notifications',
19+
])
20+
1121
linkage = ENV['USE_FRAMEWORKS']
1222
if linkage != nil
1323
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green

apps/mobile/ios/Podfile.lock

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,92 +2311,92 @@ EXTERNAL SOURCES:
23112311
:path: "../../../node_modules/react-native/ReactCommon/yoga"
23122312

23132313
SPEC CHECKSUMS:
2314-
AsyncStorage: 0a927dc82ea8eaa0350779b37d73b11d070ea677
2314+
AsyncStorage: b7e9d8feded2a33e90a600565c35fcf9fcc37543
23152315
FBLazyVector: e97c19a5a442429d1988f182a1940fb08df514da
23162316
hermes-engine: 804c3c2d60b4d0e84c847adbe8006ed6074bcaa2
23172317
JitsiWebRTC: b47805ab5668be38e7ee60e2258f49badfe8e1d0
23182318
RCTDeprecation: af44b104091a34482596cd9bd7e8d90c4e9b4bd7
23192319
RCTRequired: bb77b070f75f53398ce43c0aaaa58337cebe2bf6
23202320
RCTSwiftUI: afc0a0a635860da1040a0b894bfd529da06d7810
2321-
RCTSwiftUIWrapper: cbb32eb90f09bd42ea9ed1eecd51fef3294da673
2321+
RCTSwiftUIWrapper: 3197c020094f3b2151bb2d1223f7276787be8166
23222322
RCTTypeSafety: d13e192a37f151ce354641184bf4239844a3be17
23232323
React: 1ba7d364ade7d883a1ec055bfc3606f35fdee17b
23242324
React-callinvoker: bc2a26f8d84fb01f003fc6de6c9337b64715f95b
2325-
React-Core: bdaa87b276ca31877632a982ecf7c36f8c826414
2325+
React-Core: 7840d3a80b43a95c5e80ef75146bd70925ebab0f
23262326
React-Core-prebuilt: 67f423ba104169c581889bd3f9a6cdcbe1530b18
2327-
React-CoreModules: b24989f62d56390ae08ca4f65e6f38fe6802de42
2328-
React-cxxreact: 1a2dfcbc18a6b610664dba152adf327f063a0d12
2327+
React-CoreModules: 2eb010400b63b89e53a324ffb3c112e4c7c3ce42
2328+
React-cxxreact: a558e92199d26f145afa9e62c4233cf8e7950efe
23292329
React-debug: 755200a6e7f5e6e0a40ff8d215493d43cce285fc
2330-
React-defaultsnativemodule: 027cad46a2847719b5d3d20dd915463b06a5d4d1
2331-
React-domnativemodule: 5ddfc6b3b73b48a31dfa12f52d6b62527f6f260c
2332-
React-Fabric: 6ffcc768e2378e84ed428069c7e2d270ee78f2bf
2333-
React-FabricComponents: ee6614287222dd4f04fdb1263d1ae6eb7fe952c6
2334-
React-FabricImage: ab05740a08ad9e23e4e1701e9c354e9a9b048063
2335-
React-featureflags: a8b0c8d9a93b5903f7620408659de160d95e4efe
2336-
React-featureflagsnativemodule: 0f0fe1a044829f31d7565a4bdfded376fbcfdfc1
2337-
React-graphics: c497dd295c88729525a4752d524d2d783aa205d4
2338-
React-hermes: c2bde95033e6df1599b5c1b6d7e45736a8aa5cba
2339-
React-idlecallbacksnativemodule: 6ceacabe93be052bbe822fb018602f63a8e280e2
2340-
React-ImageManager: 820fe1d55add59ec053099a0c5abe830ecd6c699
2341-
React-intersectionobservernativemodule: f84958aaf662f95f837dc4d26cbb5e7dcc4b8f09
2342-
React-jserrorhandler: 390c6c46e2f639b5ba104385d7fba848396347e8
2343-
React-jsi: 382de7964299bbf878458006a14f52cb66a36cfc
2344-
React-jsiexecutor: b781400a9becfb24e36ac063dccb42a52dcb44ca
2345-
React-jsinspector: 0644f32cc9b09eae2bc845ceb58d03420ae70821
2346-
React-jsinspectorcdp: 96677569865afe25c737889e02d635db26131d9f
2347-
React-jsinspectornetwork: 28c7cac2e92b1739561dcffd07f5554e54050a85
2348-
React-jsinspectortracing: 58ee96f9580a143011f8b914ad6927b5116461a7
2349-
React-jsitooling: bc79639489d610c35731dd26e8e54c37e078996d
2350-
React-jsitracing: 1bb9fae4f2ccf891255a419cdfc13372d07ef4a5
2351-
React-logger: 517377b1d2ba7ac722d47fb2183b98de86632063
2352-
React-Mapbuffer: 45e088dfb58dc326ae20cca1814d3726553c4cad
2353-
React-microtasksnativemodule: ab9d1a05fe1f58ea44a97d307ef1b53463f45a3f
2354-
react-native-config: 0ac243e38516dbb8908a09eb87d76620a3083886
2355-
react-native-safe-area-context: 29044d05d61f2c60d0828c373bd0ebe17eed58d0
2356-
react-native-webrtc: b5062b745a26c99835efdf0d6c027c9b2ee7ddbc
2357-
React-NativeModulesApple: b94faa2dce6d8c0a9d722ed7ee27b996d28b62d1
2358-
React-networking: e409d8fb062162da6293e98b77f8d80cf4430e07
2330+
React-defaultsnativemodule: bb85b1bdd9b4b82650cfa92998567fcfdb030145
2331+
React-domnativemodule: ffdba8ba4323387e821d8298be2013516036df87
2332+
React-Fabric: 8705ba7f14acf5b1df474d1af4b0191c69ac4690
2333+
React-FabricComponents: 5a1b5007fe8c5d5f043e45c335ebef2af0717fb2
2334+
React-FabricImage: e96eea6bb65b501cf5db3ce4ad057a97dea1dd69
2335+
React-featureflags: 410f6c383eb94019f63f105374d738169df291ae
2336+
React-featureflagsnativemodule: 5d6d7931ec5d4576639661c0f79169a0ae383023
2337+
React-graphics: b9b69adbe79d6944838aa304c849d78f977e9f21
2338+
React-hermes: 666c66bbc856b46dfa4b132f1c9efaa37ad419a1
2339+
React-idlecallbacksnativemodule: 785d307b9236ec9fb4ec0bcf99b34e8539d2d76e
2340+
React-ImageManager: daeef8b1c19803c71a9233f21f7f235cd3ec294e
2341+
React-intersectionobservernativemodule: c17189d2350205012682aefe566f7ebaa4f980e3
2342+
React-jserrorhandler: 431377b3d1783127bc394986fe8d932bcb8982fe
2343+
React-jsi: 33db13b95bb53827b03d9fb0f567d12b63dfc8ef
2344+
React-jsiexecutor: 49de4d48a7c4f283eaa865f7a4f3919f2c39be1c
2345+
React-jsinspector: 3ec7dd478806a0eb4ec6ab394e51a395e8f895ba
2346+
React-jsinspectorcdp: bcb79a666959b1a3e8aac3ff8209d05c719aaa2a
2347+
React-jsinspectornetwork: be3b81a6342b56c74dd0bdd58bf3f62f978c1472
2348+
React-jsinspectortracing: 293251deadf7c255da593bf1d9d337a645abdfdc
2349+
React-jsitooling: 6f729cdb85ff0c8294709ec1903e1f0c59662331
2350+
React-jsitracing: be95d903cc9440ab89a704c999dd7db6675dc47f
2351+
React-logger: b5521614afb8690420146dfc61a1447bb5d65419
2352+
React-Mapbuffer: f4ee8c62e0ef8359d139124f35a471518a172cd3
2353+
React-microtasksnativemodule: d1956f0eec54c619b63a379520fb4c618a55ccb9
2354+
react-native-config: 530d1344e93c66f9819d0e5b54bd19d0d88e398c
2355+
react-native-safe-area-context: ae7587b95fb580d1800c5b0b2a7bd48c2868e67a
2356+
react-native-webrtc: e8f0ce746353adc2744a2b933645e1aeb41eaa74
2357+
React-NativeModulesApple: 5ba0903927f6b8d335a091700e9fda143980f819
2358+
React-networking: 3a4b7f9ed2b2d1c0441beacb79674323a24bcca6
23592359
React-oscompat: ff26abf0ae3e3fdbe47b44224571e3fc7226a573
2360-
React-perflogger: 757c8c725cc20e94eba406885047f03cf83044fb
2361-
React-performancecdpmetrics: fec7e28b711c95ccb6fc7e3bb16572d88bcf27ae
2362-
React-performancetimeline: 4c6102f19df01db35c37a3e63a058cfbf1a056d9
2360+
React-perflogger: a86b2146936f2ffa80188425c6e8892729e2ad01
2361+
React-performancecdpmetrics: 65b699c3e52c0a1d978d9cdf15e60c62e335b900
2362+
React-performancetimeline: b44f82caa563e46068d02d9cf5b0d2b84bdc7a6a
23632363
React-RCTActionSheet: fc1d5d419856868e7f8c13c14591ed63dadef43a
2364-
React-RCTAnimation: 1ce166ec15ab1f8eca8ebaae7f8f709d9be6958c
2365-
React-RCTAppDelegate: c752d93f597168a9a4d5678e9354bbb8d84df6d1
2366-
React-RCTBlob: 147d41ee9f80cf27fe9b2f7adc1d6d24f68ec3fc
2367-
React-RCTFabric: 712c4ad749a43712609011d178234c90a17cde12
2368-
React-RCTFBReactNativeSpec: 032ea8783dc27290ec6b9af9d8df5351847539a2
2369-
React-RCTImage: fd39f1c478f1e43357bc72c2dbdc2454aafe4035
2370-
React-RCTLinking: 02ca1c83536dab08130f5db4852f293c53885dd6
2371-
React-RCTNetwork: 85dc64c530e4b0be7436f9a15b03caba24e9a3a1
2372-
React-RCTRuntime: c75950caa80e6884cbf0417d8738992256890508
2373-
React-RCTSettings: df5da31865cc1bab7ef5314e65ca18f6b538d71d
2374-
React-RCTText: 41587e426883c9a83fd8eb0c57fe328aad4ed57a
2375-
React-RCTVibration: 8ca2f9839c53416dffb584adb94501431ba7f96e
2364+
React-RCTAnimation: 2a1e7eeb55b71e8524db296fa31e46eeaa2d0da4
2365+
React-RCTAppDelegate: 317c1102a2d0bcc07567d8de58d9147102080b0e
2366+
React-RCTBlob: c82bbf96b2d1389550c05fb9739d4e85d471052e
2367+
React-RCTFabric: d82ad8120ba70fe63207e261b9e33d9b6077e2de
2368+
React-RCTFBReactNativeSpec: 4d33b5f3b339e9fa902168e8a1d62c458dda16e9
2369+
React-RCTImage: f959463e53ea731ab267e371eea1b92fccf27634
2370+
React-RCTLinking: 2a857113b8059ac4f0a8ae89f8aa312837b955d0
2371+
React-RCTNetwork: dc026bf25f6457249349be8cf8bd5fdf84cdfb14
2372+
React-RCTRuntime: b0630731fd864064066301e1e31406fea606d5f9
2373+
React-RCTSettings: e159e19475df2e92fd3b4ddcfe29f6cc12cf0ee4
2374+
React-RCTText: 7356cc84c2ed79635931891c176f72e0289dc75d
2375+
React-RCTVibration: 77621175b67b22e655ce4b29d1cda8498f2033e7
23762376
React-rendererconsistency: e91aba4bb482dac127ad955dba6333a8af629c5b
2377-
React-renderercss: 1f15a79f3cc3c9416902b8f70266408116d93bd0
2378-
React-rendererdebug: 77dcf1490ee5c0ce141d2b1eaceed02aa0996826
2379-
React-RuntimeApple: 1074835708500a69770b713f718400137f30ce7a
2380-
React-RuntimeCore: 148db945742d7ce2985cc35b8ddc61edfdb46e6d
2381-
React-runtimeexecutor: 5742146dac0f8de9c21f5f703993df249c046d0d
2382-
React-RuntimeHermes: a5bb378bea92d526341a65afa945a38c9bc787b2
2383-
React-runtimescheduler: 91838dd32460920ed1b4da68590a2684b784aacc
2384-
React-timing: 9c0e2b1532317148fa0487bbc3833c1f348981a0
2385-
React-utils: 2f8dd43fed5c6d881ac5971666bbb34cc4a03fa1
2386-
React-webperformancenativemodule: afbee7a9fd0b5bf92f6765eb41767f865b293bcc
2387-
ReactAppDependencyProvider: 26bbf1e26768d08dd965a2b5e372e53f67b21fee
2388-
ReactCodegen: 439eae7164a2e4d8ad6ee5c9ea31ac8f407b750d
2389-
ReactCommon: 309419492d417c4cbb87af06f67735afa40ecb9d
2377+
React-renderercss: 7cc41efaecf557d7b70edaa08fad5ace79f714f6
2378+
React-rendererdebug: 0f004cbed7b4c27327423be47209770830bf3c6d
2379+
React-RuntimeApple: 6f4ff8e2d8b05cb3ceabf57e494c04da8751f009
2380+
React-RuntimeCore: 25be9c7025eabb524cd00dbb6ce56d6b122e3d92
2381+
React-runtimeexecutor: 84d394b9f0a8fc7dab8a98bf88a43228bb04dac2
2382+
React-RuntimeHermes: 2bed5b2d2419945cc5c2f6d627a1b46ce3a0f66a
2383+
React-runtimescheduler: 23b092dbcd3088f9c947551c23366dd00254caf3
2384+
React-timing: 2ab9ccd4b41aa171090c16f664f6c5bfb2fd0ddc
2385+
React-utils: 8d888b379f0808bfabaea03d85f9e8dd9b8548da
2386+
React-webperformancenativemodule: c10016db7f1bb1153060d4aa9f7dbde2c88c845d
2387+
ReactAppDependencyProvider: e96e93b493d8d86eeaee3e590ba0be53f6abe46f
2388+
ReactCodegen: f66521b131699d6af0790f10653933b3f1f79a6f
2389+
ReactCommon: 07572bf9e687c8a52fbe4a3641e9e3a1a477c78e
23902390
ReactNativeDependencies: 47a8b90a868f04348dfc51b43aee063b5c214eac
2391-
ReactNativeIncallManager: 65a85aed033c1d9ec66f98a943cca51c61a210e9
2392-
RNCallKeep: 94bbe46b807ccf58e9f6ec11bc4d6087323a1954
2393-
RNGestureHandler: 6d378fd1aa991c7ab62a4215ee6cc417895a6954
2394-
RNPermissions: 0f534e5ffc883b83ba3c3cbe603481854e21130f
2395-
RNScreens: 088d923c4327c63c9f8c942cae17a9d038f47d97
2396-
RNSVG: 13970bfde0ea9c9e10e01ab0d7b4a6cde11fca1b
2397-
RNVoipPushNotification: a6f7c09e1ca7220e2be1c45e9b6b897c9600024b
2398-
Yoga: 7c1c3b93e408ac46c7ed64b5641ca7161747378d
2391+
ReactNativeIncallManager: dccd3e7499caa3bb73d3acfedf4fb0360f1a87d5
2392+
RNCallKeep: 1930a01d8caf48f018be4f2db0c9f03405c2f977
2393+
RNGestureHandler: 07de6f059e0ee5744ae9a56feb07ee345338cc31
2394+
RNPermissions: 4539112beabdf19c5023198d33877dddc6d1c5de
2395+
RNScreens: 6cb648bdad8fe9bee9259fe144df95b6d1d5b707
2396+
RNSVG: 507bf2685de6b3d49449efd4aae7e7471bb9c433
2397+
RNVoipPushNotification: 4998fe6724d421da616dca765da7dc421ff54c4e
2398+
Yoga: c0b3f2c7e8d3e327e450223a2414ca3fa296b9a2
23992399

2400-
PODFILE CHECKSUM: 21e4b7007eed8f5a51d4edb11d3bcab58ee54b32
2400+
PODFILE CHECKSUM: f4363af445117a2c2f735b7e5f4502d634b6e656
24012401

2402-
COCOAPODS: 1.15.2
2402+
COCOAPODS: 1.16.2

apps/mobile/react-native.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const {bundleCommand, startCommand} = require('@react-native/community-cli-plugin');
2+
3+
module.exports = {
4+
commands: [bundleCommand, startCommand],
5+
};

apps/mobile/src/screens/call/ActiveCallScreen.tsx

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Dimensions,
99
PanResponder,
1010
} from 'react-native';
11+
import {RTCView} from 'react-native-webrtc';
1112
import {useSafeAreaInsets} from 'react-native-safe-area-context';
1213
import {CallControls} from '../../components/CallControls';
1314
import {useCallContext} from '../../stores/callStore';
@@ -33,6 +34,25 @@ export function ActiveCallScreen({
3334
const [speakerOn, setSpeakerOn] = useState(false);
3435
const [controlsVisible, setControlsVisible] = useState(true);
3536
const [elapsed, setElapsed] = useState(0);
37+
const [localStreamUrl, setLocalStreamUrl] = useState<string | null>(null);
38+
const [remoteStreamUrl, setRemoteStreamUrl] = useState<string | null>(null);
39+
40+
// Poll for streams becoming available
41+
useEffect(() => {
42+
const interval = setInterval(() => {
43+
const local = callManager?.mediaService.getStream();
44+
const remote = callManager?.webrtcService.getRemoteStream();
45+
if (local) {
46+
const url = local.toURL();
47+
setLocalStreamUrl(prev => prev !== url ? url : prev);
48+
}
49+
if (remote) {
50+
const url = remote.toURL();
51+
setRemoteStreamUrl(prev => prev !== url ? url : prev);
52+
}
53+
}, 500);
54+
return () => clearInterval(interval);
55+
}, [callManager]);
3656

3757
const controlsOpacity = useRef(new Animated.Value(1)).current;
3858
const hideTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
@@ -148,9 +168,18 @@ export function ActiveCallScreen({
148168
return (
149169
<TouchableWithoutFeedback onPress={handleTap}>
150170
<View style={styles.container}>
151-
{/* Remote video placeholder */}
171+
{/* Remote video */}
152172
<View style={styles.remoteVideo}>
153-
<Text style={styles.placeholderText}>{contactName}</Text>
173+
{remoteStreamUrl ? (
174+
<RTCView
175+
streamURL={remoteStreamUrl}
176+
style={StyleSheet.absoluteFill}
177+
objectFit="cover"
178+
zOrder={0}
179+
/>
180+
) : (
181+
<Text style={styles.placeholderText}>{contactName}</Text>
182+
)}
154183
</View>
155184

156185
{/* Local video PiP */}
@@ -165,6 +194,14 @@ export function ActiveCallScreen({
165194
<View style={styles.localVideoInner}>
166195
{cameraOff ? (
167196
<Text style={styles.cameraOffText}>Camera off</Text>
197+
) : localStreamUrl ? (
198+
<RTCView
199+
streamURL={localStreamUrl}
200+
style={StyleSheet.absoluteFill}
201+
objectFit="cover"
202+
zOrder={1}
203+
mirror
204+
/>
168205
) : (
169206
<Text style={styles.pipLabel}>You</Text>
170207
)}

apps/mobile/src/screens/main/ContactsScreen.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import React, {useEffect, useMemo, useState} from 'react';
2-
import {View, Text, SectionList, TouchableOpacity, StyleSheet} from 'react-native';
2+
import {View, Text, SectionList, TouchableOpacity, StyleSheet, Alert} from 'react-native';
33
import Svg, {Path} from 'react-native-svg';
44
import {useSafeAreaInsets} from 'react-native-safe-area-context';
55
import {ContactRow} from '../../components/ContactRow';
66
import {SearchBar} from '../../components/SearchBar';
77
import {EmptyState} from '../../components/EmptyState';
88
import {useContacts} from '../../stores/contactsStore';
9+
import {useCallContext} from '../../stores/callStore';
910
import type {Contact} from '../../services/user/ContactsService';
1011
import {colors} from '../../theme/colors';
1112
import {typography} from '../../theme/typography';
@@ -63,6 +64,7 @@ export function ContactsScreen({navigation}: MainTabScreenProps<'Contacts'>) {
6364
const insets = useSafeAreaInsets();
6465
const [search, setSearch] = useState('');
6566
const {contacts, fetchContacts} = useContacts();
67+
const {startCall} = useCallContext();
6668

6769
useEffect(() => { fetchContacts(); }, [fetchContacts]);
6870

@@ -122,10 +124,8 @@ export function ContactsScreen({navigation}: MainTabScreenProps<'Contacts'>) {
122124
})
123125
}
124126
onCall={() =>
125-
navigation.navigate('OutgoingCall', {
126-
contactId: item.contact_user_id,
127-
contactName: item.profile?.display_name ?? '?',
128-
})
127+
startCall(item.contact_user_id, item.profile?.display_name ?? '?')
128+
.catch((err: Error) => Alert.alert('Call failed', err.message))
129129
}
130130
/>
131131
)}

0 commit comments

Comments
 (0)