diff --git a/packages/react-native-sdk/src/components/Call/CallControls/AcceptCallButton.tsx b/packages/react-native-sdk/src/components/Call/CallControls/AcceptCallButton.tsx index 03188e46ea..6ecce1bae2 100644 --- a/packages/react-native-sdk/src/components/Call/CallControls/AcceptCallButton.tsx +++ b/packages/react-native-sdk/src/components/Call/CallControls/AcceptCallButton.tsx @@ -1,6 +1,6 @@ import { useCall } from '@stream-io/video-react-bindings'; import { getLogger } from '@stream-io/video-client'; -import React from 'react'; +import React, { useState } from 'react'; import { CallControlsButton } from './CallControlsButton'; import { IconWrapper, Phone } from '../../../icons'; import { useTheme } from '../../../contexts/ThemeContext'; @@ -38,7 +38,10 @@ export const AcceptCallButton = ({ acceptCallButton, }, } = useTheme(); + const [isLoading, setIsLoading] = useState(false); + const acceptCallHandler = async () => { + setIsLoading(true); if (onPressHandler) { onPressHandler(); return; @@ -51,6 +54,8 @@ export const AcceptCallButton = ({ } catch (error) { const logger = getLogger(['AcceptCallButton']); logger('error', 'Error joining Call', error); + } finally { + setIsLoading(false); } }; @@ -60,6 +65,7 @@ export const AcceptCallButton = ({ color={colors.buttonSuccess} size={buttonSizes.md} style={acceptCallButton} + disabled={isLoading} > diff --git a/packages/react-native-sdk/src/components/Call/CallControls/RejectCallButton.tsx b/packages/react-native-sdk/src/components/Call/CallControls/RejectCallButton.tsx index a27c009ce4..51f93bff78 100644 --- a/packages/react-native-sdk/src/components/Call/CallControls/RejectCallButton.tsx +++ b/packages/react-native-sdk/src/components/Call/CallControls/RejectCallButton.tsx @@ -1,5 +1,5 @@ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings'; -import React from 'react'; +import React, { useState } from 'react'; import { CallControlsButton } from './CallControlsButton'; import { IconWrapper, PhoneDown } from '../../../icons'; import { CallingState, getLogger } from '@stream-io/video-client'; @@ -57,7 +57,10 @@ export const RejectCallButton = ({ variants: { buttonSizes, iconSizes }, }, } = useTheme(); + const [isLoading, setIsLoading] = useState(false); + const rejectCallHandler = async () => { + setIsLoading(true); if (onPressHandler) { onPressHandler(); return; @@ -73,6 +76,8 @@ export const RejectCallButton = ({ } catch (error) { const logger = getLogger(['RejectCallButton']); logger('error', 'Error rejecting Call', error); + } finally { + setIsLoading(false); } }; @@ -84,6 +89,7 @@ export const RejectCallButton = ({ // TODO: check what to do about this random style prop // svgContainerStyle={theme.icon.lg} style={rejectCallButton} + disabled={isLoading} > diff --git a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx index d71bb8c128..15e2502fc2 100644 --- a/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx +++ b/packages/react-native-sdk/src/components/Call/CallLayout/CallParticipantsGrid.tsx @@ -52,11 +52,16 @@ export const CallParticipantsGrid = ({ const { theme: { colors, callParticipantsGrid }, } = useTheme(); - const { useRemoteParticipants, useParticipants, useLocalParticipant } = - useCallStateHooks(); + const { + useRemoteParticipants, + useParticipants, + useLocalParticipant, + useDominantSpeaker, + } = useCallStateHooks(); const _remoteParticipants = useRemoteParticipants(); const localParticipant = useLocalParticipant(); const _allParticipants = useParticipants(); + const dominantSpeaker = useDominantSpeaker(); // we debounce the participants arrays to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously const remoteParticipants = useDebouncedValue(_remoteParticipants, 300); const allParticipants = useDebouncedValue(_allParticipants, 300); @@ -80,7 +85,7 @@ export const CallParticipantsGrid = ({ if (isInPiPMode) { participants = remoteParticipants.length > 0 - ? [remoteParticipants[0] as StreamVideoParticipant] + ? [dominantSpeaker || (remoteParticipants[0] as StreamVideoParticipant)] : localParticipant ? [localParticipant] : []; diff --git a/sample-apps/react-native/dogfood/ios/Podfile.lock b/sample-apps/react-native/dogfood/ios/Podfile.lock index 032db266ee..85c72cc5fa 100644 --- a/sample-apps/react-native/dogfood/ios/Podfile.lock +++ b/sample-apps/react-native/dogfood/ios/Podfile.lock @@ -1728,6 +1728,8 @@ PODS: - React-Core - RNCPushNotificationIOS (1.11.0): - React-Core + - RNDeviceInfo (14.0.1): + - React-Core - RNGestureHandler (2.21.2): - DoubleConversion - glog @@ -2010,7 +2012,7 @@ PODS: - stream-react-native-webrtc (125.0.0-rc.2): - React-Core - WebRTC-SDK (~> 125.6422.05) - - stream-video-react-native (1.4.0): + - stream-video-react-native (1.4.2): - DoubleConversion - glog - hermes-engine @@ -2110,6 +2112,7 @@ DEPENDENCIES: - RNCallKeep (from `../node_modules/react-native-callkeep`) - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" - "RNCPushNotificationIOS (from `../node_modules/@react-native-community/push-notification-ios`)" + - RNDeviceInfo (from `../node_modules/react-native-device-info`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)" - "RNNotifee (from `../node_modules/@notifee/react-native`)" @@ -2281,6 +2284,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-clipboard/clipboard" RNCPushNotificationIOS: :path: "../node_modules/@react-native-community/push-notification-ios" + RNDeviceInfo: + :path: "../node_modules/react-native-device-info" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNGoogleSignin: @@ -2321,90 +2326,91 @@ SPEC CHECKSUMS: GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 hermes-engine: 3852e37f6158a2fcfad23e31215ed495da3a6a40 - RCT-Folly: 84578c8756030547307e4572ab1947de1685c599 + RCT-Folly: bf5c0376ffe4dd2cf438dcf86db385df9fdce648 RCTDeprecation: d575d28132f93e5deef4849d5afffb4ac4e63226 RCTRequired: e2e5df1df76aac8685aabfebca389e6bec64792b RCTTypeSafety: 30e36ceafa26979860e13fb3f234fb61692924c2 React: 10ad41b51f981992714011b6a4e081234c28dc2e React-callinvoker: 58b51494f8b2cca07a27fc6f69273239c30a1e70 React-Codegen: 4b8b4817cea7a54b83851d4c1f91f79aa73de30a - React-Core: 7a5e9897daf0189c0233b25243d6704e5b9025d8 - React-CoreModules: 09d4f4ddd85ce9301c4b06dfe68750a82ee4b4f5 - React-cxxreact: 29bfe097a993c73a314f569998fe863eb6fb8a18 + React-Core: 54860c16fb5873a6f00dd44d8979bbb648b34c7c + React-CoreModules: 443101f113a7b5d51b93e061574dcadf7850f8cc + React-cxxreact: 5407ecb854a755de34c0e6b03965d3a51c28c933 React-debug: a01fc4f077430d1a73f333ee2838f4d766e2d58b - React-defaultsnativemodule: 421fc755e08d5ad7726a252cc38f6925cd2bf919 - React-domnativemodule: 64a00466588556262f7a238f0600d201a5d27b86 - React-Fabric: 22f4287daa4187e2a10f9742dc74f3af9d9b2254 - React-FabricComponents: 9295f2fabf5495c87621cea38cbd4dc445f43650 - React-FabricImage: 5caf84d721e28747c53823825a551528c20ff875 + React-defaultsnativemodule: fad7dd0129357e9012b734d641bdd7c7d8b95c8c + React-domnativemodule: b026c1578ffaada6c62ed8453a44ba263743830c + React-Fabric: 5ffd7ec9e7bf3d9e98358cbfbb1ef1b26954b6f5 + React-FabricComponents: 6306fe9587c4a460017595749f6bfd7979817f43 + React-FabricImage: 189f860814c8d2581ddc784d08addcb2e86ba5be React-featureflags: 0845d47c3314ba87f2f6315bd33d6be8d23d2be8 - React-featureflagsnativemodule: 6220f08c9c51a407375e5025421b06b7aa696ca0 - React-graphics: d9f0317c34579ce4f14d9933b8033fe9ef61c72b - React-hermes: ab8705477c497a5839966bd57471ee3611f864f8 - React-idlecallbacksnativemodule: 28c85b4c689eccc6d3fffe6fb5d010a18f48f629 - React-ImageManager: 9005e783cfae8c521d59c33b8a4df2b78d3a6345 - React-jserrorhandler: a14500014e8cd4d2f960cf16b69d2edbd32547ff - React-jsi: de2c6119671b281671fabf9e96eb11110207fe9d - React-jsiexecutor: 936132921f4d991af7b4faa7424fc54e67791dd0 - React-jsinspector: 12d33a2f643ea2fd08ff4443f3b6c8b9fc5c4976 - React-jsitracing: 1724696aadc78fca5c66ec8d2ce3b8f04d2799bc - React-logger: addd140841248966c2547eb94836399cc1061f4d - React-Mapbuffer: 1bc8e611871f4965dac0bc47a4561421a6e20f69 - React-microtasksnativemodule: cff02bc87f8a1d5b9985c1c92ea8e84e854229d9 - react-native-blob-util: 4f935148b217389fff952096c0f1a6ff67f4bdea - react-native-image-picker: 5c4cfe25630a6ec9105c16693abe8373a6f36d9a - react-native-mmkv: 36d57903d6b78677f6b7ec90c900df6e43d7d3e4 - react-native-netinfo: a65f803f0e7dfa2fd70d093546161357d9326581 - react-native-safe-area-context: ee4151c59dc010b5211fe68eca73b0f98d17b224 - react-native-video: 6d495634696a5580559828544e3454c6ac27fbd7 + React-featureflagsnativemodule: 5dfb68d7678e0fa395deac55f2a1b241a7a9dbd5 + React-graphics: e8288c4a68627349c834eaf2fcc92108f2dbefb6 + React-hermes: 8f31f252aff98a4cb711ccf6644cccfe35d8edd1 + React-idlecallbacksnativemodule: a6780c405376fa6db47619deb44ff3f8dec6c3c9 + React-ImageManager: 0b553a8e762b75d4cf9176474629f2d39cdb2aad + React-jserrorhandler: 2c47610e18594ed6b9c52995308afdbec0f59b71 + React-jsi: b96853ac12c1dab5fe3ea131f959fda0bbaf1151 + React-jsiexecutor: e38748a0e9d899f63dec562f93ac06c7acbc813d + React-jsinspector: b707427ae4772f34dab943a7343feddb155e8add + React-jsitracing: f4028bf2f09cd8707ad8befb35a8b78221d1673d + React-logger: 81d58ca6f1d93fca9a770bda6cc1c4fbfcc99c9c + React-Mapbuffer: b9bfad03a24c3ff440557e9011a6a09864849eae + React-microtasksnativemodule: 853dae5be1372b3ab52b21e29f86f2e1a0c61f37 + react-native-blob-util: 356047c561b3506396852bc0d7988243f74dd77d + react-native-image-picker: df6597d4b1878a443796be11eb2b7286ed10ece6 + react-native-mmkv: fb501d25ce65d16a1fad3296f7fc69150a1f0788 + react-native-netinfo: 299dad906cdbf3b67bcc6f693c807f98bdd127cc + react-native-safe-area-context: f826417dadd1c1042c59355fb050682a9839f990 + react-native-video: e5e752b62458690667276df947aee93b394d3e20 React-nativeconfig: 7f8cd6cae21f8bb18c53b746c495e72795fc5cb0 - React-NativeModulesApple: 3210b7177c11145bb8e0d6f24aae102a221c4ddc - React-perflogger: c8860eaab4fe60d628b27bf0086a372c429fc74f - React-performancetimeline: 6b072ee07b20f71ca7aa443d7c78b9cb2a934ead + React-NativeModulesApple: 4fb24980fec9a94c9e9c1de3cdfd38ff3b87361c + React-perflogger: f2c94413cfad44817c96cab33753831e73f0d0dd + React-performancetimeline: c3ad160557d7406ceb5bb4dbc62834b1e61ee797 React-RCTActionSheet: 2eb26cbf384f3d3b2cb2e23be850a956d83f77ab - React-RCTAnimation: aa0a663829963ca72f4c722e71bd5debbecc1348 - React-RCTAppDelegate: 12688b64e1e28e0eb1c628690678ae5d3ab356b4 - React-RCTBlob: bef788ef3433170f9748d0e00d1afc7be64bc51d - React-RCTFabric: 5f335f0643a84dd888bf7ba70d6d60484c981d87 - React-RCTImage: a9de66d305fa02008759a2aa5a723b68d18907e5 - React-RCTLinking: 15fe8ccad84a4a5274d55b9d43e223896718772d - React-RCTNetwork: 7635ab6b7617648e5b5e35cdb3a4edab6fa309a6 - React-RCTSettings: 18e666705ea62aac59f2a8d50ced87b9b8902c7b - React-RCTText: 5cf76f649b4781362d23f9ee3d52e8d12a74dd18 - React-RCTVibration: bd72dc267866c8cd524c9a61d15060949ff24cf9 + React-RCTAnimation: 59463699a92edc6705ce5306bb789d6a0ca4df0b + React-RCTAppDelegate: 4d9efca7caa477b106e3d55af339d0e071441536 + React-RCTBlob: 0883f5363069ad30f628c970fcb413a619e42804 + React-RCTFabric: 8cd047489627f322e491cf21d91ea242c8068fe3 + React-RCTImage: 78884b7ea6ef4f7bb9655614bf09a40054f282ce + React-RCTLinking: b9beba7465fd9a1ed7a88a4e7fc403d26e17ab95 + React-RCTNetwork: 701d9c050077596b15a11b6b573ed95c309d2315 + React-RCTSettings: e700a82e3e923c10060b8f65297f9d321b93d8eb + React-RCTText: e782ce1c3f9d915daf50d97157f8c226e8f3d206 + React-RCTVibration: 2a19c56be78cb7afce9f4f3471aacfb063f32a00 React-rendererconsistency: bbb7bafd25f3a41f4ea604be846dc2da8180e840 - React-rendererdebug: 7c262ecec4bcddf7c9b8782f836fa68864d3d5f7 + React-rendererdebug: 5cd463cfe5c4c174a8aa6abd67f190ad07a03e24 React-rncore: f2e8940f20f97f900f359861adf3a96f30dc82a3 - React-RuntimeApple: e98509dfdc3c1da7560ac10637e077a05fc438d0 - React-RuntimeCore: 89bd1ffca294f5fb437466e32b394f17bae28b31 + React-RuntimeApple: 4ce7c4cc1ee24608b40a22667250e32e4171eef0 + React-RuntimeCore: c3e89760391557d91b72bba1078d3e2ce26e963d React-runtimeexecutor: 69e27948ee2127400297c7de50b809a7cd127a15 - React-RuntimeHermes: 52f1738a3864f40445b0a5362e232eba29dcbcb1 - React-runtimescheduler: 98d80589939956811f3ff51cb1ab720e6b3b1290 + React-RuntimeHermes: 446adf8034db4b8f9d53b0140fdab832e475e7c9 + React-runtimescheduler: 18e831b141db320f5ee13e0a6ecfd486a0e3de0c React-timing: 5627775f1ccecc1d56bfc1247f942eec82069d1f - React-utils: 7ce63e32e4ca425cc73cfb84e656bfb9e02e58b3 - ReactCodegen: 76542015d808938c67640540879b99413001fe42 - ReactCommon: a1c914f7575011239a63603a95fb341d0331953c - ReactNativeIncallManager: ef7b845e166f04cf8ddf433d8a23227b01cef87a - RNCallKeep: 516281f03461e6be68f21a4634dbeee85d3fb730 - RNCClipboard: efe1b27ad1ea378c60c8c8aabfd130961bbeb474 - RNCPushNotificationIOS: 6c4ca3388c7434e4a662b92e4dfeeee858e6f440 - RNGestureHandler: 932e0f07ccf470940afa9d0a8b6d8221e7e19cff - RNGoogleSignin: 9b083b6a575e7c2401aac339c93f8292d0542d29 - RNNotifee: 52b319634ba89a2eacdfbadc01e059fd18505f04 - RNPermissions: a9ea34e4f88ced1f9664d6589f57b4931b23e3aa - RNReactNativeHapticFeedback: d557285118551f215efe4b8fbbd24d2c4ae6c1b9 - RNReanimated: 9ebacf9fbe1b8aeb2fc19de93d4a6779b89d0b89 - RNScreens: d6b3735a362dab6a8cef14d032bdbdaf6e1b8dfa - RNSVG: b24f7dfe496d5463a79b330713226587e4b13956 - RNVoipPushNotification: e5edde96849c0133ebc7e900dc30c8c221cfb690 + React-utils: 2431220eeebc884010eb8df65335cb16c5849a55 + ReactCodegen: 7ffe695604dd4aa69ac6d1baa4e51d2f1a9c610f + ReactCommon: 555c6f17f322bf4e7b9ce48990b252723170d158 + ReactNativeIncallManager: bfc9c67358cd524882a7c4116dcb311ac2293d4b + RNCallKeep: 7bfa8f502067be6650eeca5ec0ebbf795314c5c3 + RNCClipboard: 3f0451a8100393908bea5c5c5b16f96d45f30bfc + RNCPushNotificationIOS: 64218f3c776c03d7408284a819b2abfda1834bc8 + RNDeviceInfo: 825f0d2f4381327317f12d1522720a8f76e6a19e + RNGestureHandler: 15ee1ab573a954c92641877ca946e2680f2e58da + RNGoogleSignin: fc408799f1990a12497a32f64280c0fe353ffcc1 + RNNotifee: bc20a5e3d581f629db988075944fdd944d363dfe + RNPermissions: 13cf580b8ac0f6e36ff8f61eb3a955dcffdbd9ab + RNReactNativeHapticFeedback: 70dd302f264d06d1a2e0632a717d0b3ed10a0f35 + RNReanimated: 7f11fff1964b5d073961b54167c22ebf3bd5aaff + RNScreens: 61099dac2e3cd356d2f7818ef15e9b6ad2769408 + RNSVG: feeb4eb546e718d6c6fb70d10fd31e0c5c2d0d90 + RNVoipPushNotification: 543e18f83089134a35e7f1d2eba4c8b1f7776b08 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - stream-chat-react-native: 470c37e2bed295da2ed9148f398694995d98f5af - stream-io-video-filters-react-native: 8e3d06506767c1a58576bb9d0229cbfc51a709d6 - stream-react-native-webrtc: 2930ee2acccc0ba9f52d320055e4b0287a7cbbbc - stream-video-react-native: 0c883e0958749d3175397b121f3176b0aae6cf86 + stream-chat-react-native: e515fc7d0951a32627f75ccca0699c139ba34579 + stream-io-video-filters-react-native: 4153f006b31f805f6a4e42556d8e4ae768231900 + stream-react-native-webrtc: c6e8ce6f886d1c4eb8bf048639176d46cfb681d6 + stream-video-react-native: 7ce50d983d650bba27a5aa9eb834d7cd9823fdba WebRTC-SDK: 1990a1a595bd0b59c17485ce13ff17f575732c12 Yoga: 513b871d622689bd53b51481bbcfb6b8f1a3de5b PODFILE CHECKSUM: 22e502ced1a8b5a5e637f60837d3de140b3387b8 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/sample-apps/react-native/dogfood/package.json b/sample-apps/react-native/dogfood/package.json index 9022c4f8c9..d6f9388f32 100644 --- a/sample-apps/react-native/dogfood/package.json +++ b/sample-apps/react-native/dogfood/package.json @@ -36,6 +36,7 @@ "react-native": "0.76.2", "react-native-blob-util": "^0.19.11", "react-native-callkeep": "4.3.11", + "react-native-device-info": "^14.0.1", "react-native-dotenv": "^3.4.11", "react-native-gesture-handler": "^2.21", "react-native-haptic-feedback": "^2.0.3", diff --git a/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx b/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx index 5d704b1fb2..3bc38a6953 100644 --- a/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx +++ b/sample-apps/react-native/dogfood/src/components/ActiveCall.tsx @@ -14,6 +14,7 @@ import { TopControls } from './CallControlls/TopControls'; import { useLayout } from '../contexts/LayoutContext'; import { useToggleCallRecording } from '@stream-io/video-react-bindings'; import { useAppGlobalStoreValue } from '../contexts/AppContext'; +import DeviceInfo from 'react-native-device-info'; type ActiveCallProps = { onHangupCallHandler?: () => void; @@ -31,11 +32,13 @@ export const ActiveCall = ({ const [isCallParticipantsVisible, setIsCallParticipantsVisible] = useState(false); const call = useCall(); - const currentOrientation = useOrientation(); const styles = useStyles(); const { selectedLayout } = useLayout(); const themeMode = useAppGlobalStoreValue((store) => store.themeMode); const isInPiPMode = useIsInPiPMode(false); + const currentOrientation = useOrientation(); + const isTablet = DeviceInfo.isTablet(); + const isLandscape = !isTablet && currentOrientation === 'landscape'; const onOpenCallParticipantsInfo = useCallback(() => { setIsCallParticipantsVisible(true); @@ -93,7 +96,7 @@ export const ActiveCall = ({ { const { t } = useI18n(); const orientation = useOrientation(); const styles = useStyles(); + const [isLoading, setIsLoading] = useState(false); const startCallHandler = useCallback(async () => { + setIsLoading(true); let ringingUserIds = !ringingUserIdsText ? ringingUsers : ringingUserIdsText.split(','); @@ -65,6 +67,8 @@ const JoinCallScreen = () => { Alert.alert('Error calling users', error.message); } console.log('Failed to createCall', error); + } finally { + setIsLoading(false); } }, [ringingUserIdsText, ringingUsers, videoClient, userId]); @@ -85,6 +89,9 @@ const JoinCallScreen = () => { flexDirection: orientation === 'landscape' ? 'row' : 'column', }; + const startCallDisabled = + (ringingUserIdsText === '' && ringingUsers.length === 0) || isLoading; + return ( { style={styles.textInputStyle} />