@stream-io/video-react-native-sdk 0.0.1-alpha.141

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/CHANGELOG.md +739 -0
  2. package/LICENSE +219 -0
  3. package/README.md +19 -0
  4. package/dist/__tests__/components/ActiveCall.test.d.ts +1 -0
  5. package/dist/__tests__/components/ActiveCall.test.js +89 -0
  6. package/dist/__tests__/components/ActiveCall.test.js.map +1 -0
  7. package/dist/__tests__/components/Avatar.test.d.ts +1 -0
  8. package/dist/__tests__/components/Avatar.test.js +34 -0
  9. package/dist/__tests__/components/Avatar.test.js.map +1 -0
  10. package/dist/__tests__/components/ParticipantView.test.d.ts +1 -0
  11. package/dist/__tests__/components/ParticipantView.test.js +66 -0
  12. package/dist/__tests__/components/ParticipantView.test.js.map +1 -0
  13. package/dist/__tests__/mocks/call.d.ts +2 -0
  14. package/dist/__tests__/mocks/call.js +21 -0
  15. package/dist/__tests__/mocks/call.js.map +1 -0
  16. package/dist/__tests__/mocks/client.d.ts +2 -0
  17. package/dist/__tests__/mocks/client.js +31 -0
  18. package/dist/__tests__/mocks/client.js.map +1 -0
  19. package/dist/__tests__/mocks/participant.d.ts +3 -0
  20. package/dist/__tests__/mocks/participant.js +25 -0
  21. package/dist/__tests__/mocks/participant.js.map +1 -0
  22. package/dist/__tests__/utils/RNTLTools.d.ts +15 -0
  23. package/dist/__tests__/utils/RNTLTools.js +54 -0
  24. package/dist/__tests__/utils/RNTLTools.js.map +1 -0
  25. package/dist/index.d.ts +9 -0
  26. package/dist/index.js +39 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/jest-setup.d.ts +1 -0
  29. package/dist/jest-setup.js +34 -0
  30. package/dist/jest-setup.js.map +1 -0
  31. package/dist/src/components/ActiveCall.d.ts +18 -0
  32. package/dist/src/components/ActiveCall.js +117 -0
  33. package/dist/src/components/ActiveCall.js.map +1 -0
  34. package/dist/src/components/Avatar.d.ts +25 -0
  35. package/dist/src/components/Avatar.js +55 -0
  36. package/dist/src/components/Avatar.js.map +1 -0
  37. package/dist/src/components/CallControlsButton.d.ts +22 -0
  38. package/dist/src/components/CallControlsButton.js +43 -0
  39. package/dist/src/components/CallControlsButton.js.map +1 -0
  40. package/dist/src/components/CallControlsView.d.ts +9 -0
  41. package/dist/src/components/CallControlsView.js +95 -0
  42. package/dist/src/components/CallControlsView.js.map +1 -0
  43. package/dist/src/components/CallParticipantsBadge.d.ts +1 -0
  44. package/dist/src/components/CallParticipantsBadge.js +48 -0
  45. package/dist/src/components/CallParticipantsBadge.js.map +1 -0
  46. package/dist/src/components/CallParticipantsInfoView.d.ts +21 -0
  47. package/dist/src/components/CallParticipantsInfoView.js +213 -0
  48. package/dist/src/components/CallParticipantsInfoView.js.map +1 -0
  49. package/dist/src/components/CallParticipantsList.d.ts +25 -0
  50. package/dist/src/components/CallParticipantsList.js +124 -0
  51. package/dist/src/components/CallParticipantsList.js.map +1 -0
  52. package/dist/src/components/CallParticipantsOptions.d.ts +8 -0
  53. package/dist/src/components/CallParticipantsOptions.js +189 -0
  54. package/dist/src/components/CallParticipantsOptions.js.map +1 -0
  55. package/dist/src/components/CallParticipantsSpotlightView.d.ts +1 -0
  56. package/dist/src/components/CallParticipantsSpotlightView.js +40 -0
  57. package/dist/src/components/CallParticipantsSpotlightView.js.map +1 -0
  58. package/dist/src/components/CallParticipantsView.d.ts +1 -0
  59. package/dist/src/components/CallParticipantsView.js +31 -0
  60. package/dist/src/components/CallParticipantsView.js.map +1 -0
  61. package/dist/src/components/IncomingCallView.d.ts +1 -0
  62. package/dist/src/components/IncomingCallView.js +98 -0
  63. package/dist/src/components/IncomingCallView.js.map +1 -0
  64. package/dist/src/components/LobbyView.d.ts +1 -0
  65. package/dist/src/components/LobbyView.js +215 -0
  66. package/dist/src/components/LobbyView.js.map +1 -0
  67. package/dist/src/components/LocalVideoView.d.ts +34 -0
  68. package/dist/src/components/LocalVideoView.js +100 -0
  69. package/dist/src/components/LocalVideoView.js.map +1 -0
  70. package/dist/src/components/NetworkQualityIndicator.d.ts +8 -0
  71. package/dist/src/components/NetworkQualityIndicator.js +36 -0
  72. package/dist/src/components/NetworkQualityIndicator.js.map +1 -0
  73. package/dist/src/components/OutgoingCallView.d.ts +1 -0
  74. package/dist/src/components/OutgoingCallView.js +103 -0
  75. package/dist/src/components/OutgoingCallView.js.map +1 -0
  76. package/dist/src/components/ParticipantReaction.d.ts +7 -0
  77. package/dist/src/components/ParticipantReaction.js +45 -0
  78. package/dist/src/components/ParticipantReaction.js.map +1 -0
  79. package/dist/src/components/ParticipantView.d.ts +42 -0
  80. package/dist/src/components/ParticipantView.js +256 -0
  81. package/dist/src/components/ParticipantView.js.map +1 -0
  82. package/dist/src/components/ReactionsModal.d.ts +7 -0
  83. package/dist/src/components/ReactionsModal.js +65 -0
  84. package/dist/src/components/ReactionsModal.js.map +1 -0
  85. package/dist/src/components/ToggleAudioButton.d.ts +1 -0
  86. package/dist/src/components/ToggleAudioButton.js +78 -0
  87. package/dist/src/components/ToggleAudioButton.js.map +1 -0
  88. package/dist/src/components/ToggleVideoButton.d.ts +1 -0
  89. package/dist/src/components/ToggleVideoButton.js +78 -0
  90. package/dist/src/components/ToggleVideoButton.js.map +1 -0
  91. package/dist/src/components/UserInfoView.d.ts +13 -0
  92. package/dist/src/components/UserInfoView.js +82 -0
  93. package/dist/src/components/UserInfoView.js.map +1 -0
  94. package/dist/src/components/VideoRenderer.d.ts +65 -0
  95. package/dist/src/components/VideoRenderer.js +18 -0
  96. package/dist/src/components/VideoRenderer.js.map +1 -0
  97. package/dist/src/components/index.d.ts +12 -0
  98. package/dist/src/components/index.js +29 -0
  99. package/dist/src/components/index.js.map +1 -0
  100. package/dist/src/constants/A11yLabels.d.ts +20 -0
  101. package/dist/src/constants/A11yLabels.js +29 -0
  102. package/dist/src/constants/A11yLabels.js.map +1 -0
  103. package/dist/src/constants/index.d.ts +11 -0
  104. package/dist/src/constants/index.js +30 -0
  105. package/dist/src/constants/index.js.map +1 -0
  106. package/dist/src/contexts/StreamVideoContext/createStoreContext.d.ts +14 -0
  107. package/dist/src/contexts/StreamVideoContext/createStoreContext.js +100 -0
  108. package/dist/src/contexts/StreamVideoContext/createStoreContext.js.map +1 -0
  109. package/dist/src/contexts/StreamVideoContext/index.d.ts +30 -0
  110. package/dist/src/contexts/StreamVideoContext/index.js +18 -0
  111. package/dist/src/contexts/StreamVideoContext/index.js.map +1 -0
  112. package/dist/src/contexts/index.d.ts +1 -0
  113. package/dist/src/contexts/index.js +18 -0
  114. package/dist/src/contexts/index.js.map +1 -0
  115. package/dist/src/hooks/index.d.ts +10 -0
  116. package/dist/src/hooks/index.js +27 -0
  117. package/dist/src/hooks/index.js.map +1 -0
  118. package/dist/src/hooks/useCallControls.d.ts +14 -0
  119. package/dist/src/hooks/useCallControls.js +148 -0
  120. package/dist/src/hooks/useCallControls.js.map +1 -0
  121. package/dist/src/hooks/useCallCycleEffect.d.ts +8 -0
  122. package/dist/src/hooks/useCallCycleEffect.js +62 -0
  123. package/dist/src/hooks/useCallCycleEffect.js.map +1 -0
  124. package/dist/src/hooks/useCallKeep.d.ts +11 -0
  125. package/dist/src/hooks/useCallKeep.js +41 -0
  126. package/dist/src/hooks/useCallKeep.js.map +1 -0
  127. package/dist/src/hooks/useCreateStreamVideoClient.d.ts +33 -0
  128. package/dist/src/hooks/useCreateStreamVideoClient.js +40 -0
  129. package/dist/src/hooks/useCreateStreamVideoClient.js.map +1 -0
  130. package/dist/src/hooks/useIncallManager.d.ts +13 -0
  131. package/dist/src/hooks/useIncallManager.js +24 -0
  132. package/dist/src/hooks/useIncallManager.js.map +1 -0
  133. package/dist/src/hooks/useLocalVideoStream.d.ts +7 -0
  134. package/dist/src/hooks/useLocalVideoStream.js +34 -0
  135. package/dist/src/hooks/useLocalVideoStream.js.map +1 -0
  136. package/dist/src/hooks/useMutingState.d.ts +12 -0
  137. package/dist/src/hooks/useMutingState.js +25 -0
  138. package/dist/src/hooks/useMutingState.js.map +1 -0
  139. package/dist/src/hooks/usePermissionNotification.d.ts +18 -0
  140. package/dist/src/hooks/usePermissionNotification.js +33 -0
  141. package/dist/src/hooks/usePermissionNotification.js.map +1 -0
  142. package/dist/src/hooks/usePermissionRequest.d.ts +1 -0
  143. package/dist/src/hooks/usePermissionRequest.js +62 -0
  144. package/dist/src/hooks/usePermissionRequest.js.map +1 -0
  145. package/dist/src/hooks/usePublishMediaStreams.d.ts +6 -0
  146. package/dist/src/hooks/usePublishMediaStreams.js +49 -0
  147. package/dist/src/hooks/usePublishMediaStreams.js.map +1 -0
  148. package/dist/src/icons/ArrowRight.d.ts +5 -0
  149. package/dist/src/icons/ArrowRight.js +9 -0
  150. package/dist/src/icons/ArrowRight.js.map +1 -0
  151. package/dist/src/icons/CameraSwitch.d.ts +5 -0
  152. package/dist/src/icons/CameraSwitch.js +9 -0
  153. package/dist/src/icons/CameraSwitch.js.map +1 -0
  154. package/dist/src/icons/Chat.d.ts +5 -0
  155. package/dist/src/icons/Chat.js +9 -0
  156. package/dist/src/icons/Chat.js.map +1 -0
  157. package/dist/src/icons/Cross.d.ts +5 -0
  158. package/dist/src/icons/Cross.js +9 -0
  159. package/dist/src/icons/Cross.js.map +1 -0
  160. package/dist/src/icons/Mic.d.ts +5 -0
  161. package/dist/src/icons/Mic.js +10 -0
  162. package/dist/src/icons/Mic.js.map +1 -0
  163. package/dist/src/icons/MicOff.d.ts +5 -0
  164. package/dist/src/icons/MicOff.js +9 -0
  165. package/dist/src/icons/MicOff.js.map +1 -0
  166. package/dist/src/icons/Participants.d.ts +5 -0
  167. package/dist/src/icons/Participants.js +9 -0
  168. package/dist/src/icons/Participants.js.map +1 -0
  169. package/dist/src/icons/Phone.d.ts +5 -0
  170. package/dist/src/icons/Phone.js +9 -0
  171. package/dist/src/icons/Phone.js.map +1 -0
  172. package/dist/src/icons/PhoneDown.d.ts +5 -0
  173. package/dist/src/icons/PhoneDown.js +10 -0
  174. package/dist/src/icons/PhoneDown.js.map +1 -0
  175. package/dist/src/icons/Pin.d.ts +5 -0
  176. package/dist/src/icons/Pin.js +9 -0
  177. package/dist/src/icons/Pin.js.map +1 -0
  178. package/dist/src/icons/Reaction.d.ts +5 -0
  179. package/dist/src/icons/Reaction.js +9 -0
  180. package/dist/src/icons/Reaction.js.map +1 -0
  181. package/dist/src/icons/ScreenShare.d.ts +5 -0
  182. package/dist/src/icons/ScreenShare.js +40 -0
  183. package/dist/src/icons/ScreenShare.js.map +1 -0
  184. package/dist/src/icons/Settings.d.ts +5 -0
  185. package/dist/src/icons/Settings.js +34 -0
  186. package/dist/src/icons/Settings.js.map +1 -0
  187. package/dist/src/icons/Spotlight.d.ts +5 -0
  188. package/dist/src/icons/Spotlight.js +9 -0
  189. package/dist/src/icons/Spotlight.js.map +1 -0
  190. package/dist/src/icons/ThreeDots.d.ts +5 -0
  191. package/dist/src/icons/ThreeDots.js +11 -0
  192. package/dist/src/icons/ThreeDots.js.map +1 -0
  193. package/dist/src/icons/Video.d.ts +5 -0
  194. package/dist/src/icons/Video.js +9 -0
  195. package/dist/src/icons/Video.js.map +1 -0
  196. package/dist/src/icons/VideoDisabled.d.ts +5 -0
  197. package/dist/src/icons/VideoDisabled.js +10 -0
  198. package/dist/src/icons/VideoDisabled.js.map +1 -0
  199. package/dist/src/icons/VideoOff.d.ts +5 -0
  200. package/dist/src/icons/VideoOff.js +10 -0
  201. package/dist/src/icons/VideoOff.js.map +1 -0
  202. package/dist/src/icons/VideoSlash.d.ts +5 -0
  203. package/dist/src/icons/VideoSlash.js +10 -0
  204. package/dist/src/icons/VideoSlash.js.map +1 -0
  205. package/dist/src/icons/index.d.ts +19 -0
  206. package/dist/src/icons/index.js +36 -0
  207. package/dist/src/icons/index.js.map +1 -0
  208. package/dist/src/providers/MediaDevices.d.ts +9 -0
  209. package/dist/src/providers/MediaDevices.js +43 -0
  210. package/dist/src/providers/MediaDevices.js.map +1 -0
  211. package/dist/src/providers/StreamCall.d.ts +81 -0
  212. package/dist/src/providers/StreamCall.js +74 -0
  213. package/dist/src/providers/StreamCall.js.map +1 -0
  214. package/dist/src/providers/StreamVideo.d.ts +10 -0
  215. package/dist/src/providers/StreamVideo.js +66 -0
  216. package/dist/src/providers/StreamVideo.js.map +1 -0
  217. package/dist/src/providers/index.d.ts +2 -0
  218. package/dist/src/providers/index.js +19 -0
  219. package/dist/src/providers/index.js.map +1 -0
  220. package/dist/src/theme/avatar.d.ts +2 -0
  221. package/dist/src/theme/avatar.js +11 -0
  222. package/dist/src/theme/avatar.js.map +1 -0
  223. package/dist/src/theme/button.d.ts +2 -0
  224. package/dist/src/theme/button.js +11 -0
  225. package/dist/src/theme/button.js.map +1 -0
  226. package/dist/src/theme/colors.d.ts +3 -0
  227. package/dist/src/theme/colors.js +49 -0
  228. package/dist/src/theme/colors.js.map +1 -0
  229. package/dist/src/theme/constants.d.ts +47 -0
  230. package/dist/src/theme/constants.js +54 -0
  231. package/dist/src/theme/constants.js.map +1 -0
  232. package/dist/src/theme/fonts.d.ts +2 -0
  233. package/dist/src/theme/fonts.js +67 -0
  234. package/dist/src/theme/fonts.js.map +1 -0
  235. package/dist/src/theme/icon.d.ts +2 -0
  236. package/dist/src/theme/icon.js +11 -0
  237. package/dist/src/theme/icon.js.map +1 -0
  238. package/dist/src/theme/index.d.ts +2 -0
  239. package/dist/src/theme/index.js +26 -0
  240. package/dist/src/theme/index.js.map +1 -0
  241. package/dist/src/theme/margin.d.ts +2 -0
  242. package/dist/src/theme/margin.js +11 -0
  243. package/dist/src/theme/margin.js.map +1 -0
  244. package/dist/src/theme/padding.d.ts +2 -0
  245. package/dist/src/theme/padding.js +11 -0
  246. package/dist/src/theme/padding.js.map +1 -0
  247. package/dist/src/theme/rounded.d.ts +2 -0
  248. package/dist/src/theme/rounded.js +11 -0
  249. package/dist/src/theme/rounded.js.map +1 -0
  250. package/dist/src/theme/spacing.d.ts +2 -0
  251. package/dist/src/theme/spacing.js +11 -0
  252. package/dist/src/theme/spacing.js.map +1 -0
  253. package/dist/src/theme/types.d.ts +45 -0
  254. package/dist/src/theme/types.js +3 -0
  255. package/dist/src/theme/types.js.map +1 -0
  256. package/dist/src/utils/StreamVideoRN.d.ts +25 -0
  257. package/dist/src/utils/StreamVideoRN.js +23 -0
  258. package/dist/src/utils/StreamVideoRN.js.map +1 -0
  259. package/dist/src/utils/hooks/index.d.ts +3 -0
  260. package/dist/src/utils/hooks/index.js +20 -0
  261. package/dist/src/utils/hooks/index.js.map +1 -0
  262. package/dist/src/utils/hooks/useAppStateListener.d.ts +1 -0
  263. package/dist/src/utils/hooks/useAppStateListener.js +41 -0
  264. package/dist/src/utils/hooks/useAppStateListener.js.map +1 -0
  265. package/dist/src/utils/hooks/useDebouncedValue.d.ts +7 -0
  266. package/dist/src/utils/hooks/useDebouncedValue.js +22 -0
  267. package/dist/src/utils/hooks/useDebouncedValue.js.map +1 -0
  268. package/dist/src/utils/hooks/usePrevious.d.ts +1 -0
  269. package/dist/src/utils/hooks/usePrevious.js +13 -0
  270. package/dist/src/utils/hooks/usePrevious.js.map +1 -0
  271. package/dist/src/utils/index.d.ts +5 -0
  272. package/dist/src/utils/index.js +53 -0
  273. package/dist/src/utils/index.js.map +1 -0
  274. package/dist/src/utils/verifyAndroidBluetoothPermissions.d.ts +1 -0
  275. package/dist/src/utils/verifyAndroidBluetoothPermissions.js +30 -0
  276. package/dist/src/utils/verifyAndroidBluetoothPermissions.js.map +1 -0
  277. package/index.ts +22 -0
  278. package/package.json +70 -0
  279. package/src/components/ActiveCall.tsx +122 -0
  280. package/src/components/Avatar.tsx +88 -0
  281. package/src/components/CallControlsButton.tsx +72 -0
  282. package/src/components/CallControlsView.tsx +128 -0
  283. package/src/components/CallParticipantsBadge.tsx +57 -0
  284. package/src/components/CallParticipantsInfoView.tsx +287 -0
  285. package/src/components/CallParticipantsList.tsx +172 -0
  286. package/src/components/CallParticipantsOptions.tsx +248 -0
  287. package/src/components/CallParticipantsSpotlightView.tsx +53 -0
  288. package/src/components/CallParticipantsView.tsx +30 -0
  289. package/src/components/IncomingCallView.tsx +133 -0
  290. package/src/components/LobbyView.tsx +236 -0
  291. package/src/components/LocalVideoView.tsx +141 -0
  292. package/src/components/NetworkQualityIndicator.tsx +72 -0
  293. package/src/components/OutgoingCallView.tsx +133 -0
  294. package/src/components/ParticipantReaction.tsx +59 -0
  295. package/src/components/ParticipantView.tsx +315 -0
  296. package/src/components/ReactionsModal.tsx +90 -0
  297. package/src/components/ToggleAudioButton.tsx +103 -0
  298. package/src/components/ToggleVideoButton.tsx +103 -0
  299. package/src/components/UserInfoView.tsx +118 -0
  300. package/src/components/VideoRenderer.tsx +85 -0
  301. package/src/components/index.ts +12 -0
  302. package/src/constants/A11yLabels.ts +24 -0
  303. package/src/constants/index.ts +28 -0
  304. package/src/contexts/StreamVideoContext/createStoreContext.tsx +117 -0
  305. package/src/contexts/StreamVideoContext/index.tsx +37 -0
  306. package/src/contexts/index.ts +1 -0
  307. package/src/hooks/index.ts +10 -0
  308. package/src/hooks/useCallControls.tsx +186 -0
  309. package/src/hooks/useCallCycleEffect.tsx +68 -0
  310. package/src/hooks/useCallKeep.tsx +51 -0
  311. package/src/hooks/useCreateStreamVideoClient.tsx +77 -0
  312. package/src/hooks/useIncallManager.tsx +23 -0
  313. package/src/hooks/useLocalVideoStream.ts +36 -0
  314. package/src/hooks/useMutingState.ts +24 -0
  315. package/src/hooks/usePermissionNotification.tsx +53 -0
  316. package/src/hooks/usePermissionRequest.tsx +67 -0
  317. package/src/hooks/usePublishMediaStreams.ts +59 -0
  318. package/src/icons/ArrowRight.tsx +16 -0
  319. package/src/icons/CameraSwitch.tsx +14 -0
  320. package/src/icons/Chat.tsx +14 -0
  321. package/src/icons/Cross.tsx +14 -0
  322. package/src/icons/Mic.tsx +18 -0
  323. package/src/icons/MicOff.tsx +14 -0
  324. package/src/icons/Participants.tsx +16 -0
  325. package/src/icons/Phone.tsx +14 -0
  326. package/src/icons/PhoneDown.tsx +15 -0
  327. package/src/icons/Pin.tsx +16 -0
  328. package/src/icons/Reaction.tsx +14 -0
  329. package/src/icons/ScreenShare.tsx +32 -0
  330. package/src/icons/Settings.tsx +13 -0
  331. package/src/icons/Spotlight.tsx +16 -0
  332. package/src/icons/ThreeDots.tsx +13 -0
  333. package/src/icons/Video.tsx +14 -0
  334. package/src/icons/VideoDisabled.tsx +22 -0
  335. package/src/icons/VideoOff.tsx +20 -0
  336. package/src/icons/VideoSlash.tsx +15 -0
  337. package/src/icons/index.tsx +19 -0
  338. package/src/providers/MediaDevices.tsx +45 -0
  339. package/src/providers/StreamCall.tsx +136 -0
  340. package/src/providers/StreamVideo.tsx +50 -0
  341. package/src/providers/index.ts +2 -0
  342. package/src/theme/avatar.ts +9 -0
  343. package/src/theme/button.ts +9 -0
  344. package/src/theme/colors.ts +49 -0
  345. package/src/theme/constants.ts +51 -0
  346. package/src/theme/fonts.ts +65 -0
  347. package/src/theme/icon.ts +9 -0
  348. package/src/theme/index.ts +25 -0
  349. package/src/theme/margin.ts +9 -0
  350. package/src/theme/padding.ts +9 -0
  351. package/src/theme/rounded.ts +9 -0
  352. package/src/theme/spacing.ts +9 -0
  353. package/src/theme/types.ts +64 -0
  354. package/src/utils/StreamVideoRN.ts +35 -0
  355. package/src/utils/hooks/index.ts +3 -0
  356. package/src/utils/hooks/useAppStateListener.ts +48 -0
  357. package/src/utils/hooks/useDebouncedValue.ts +21 -0
  358. package/src/utils/hooks/usePrevious.ts +11 -0
  359. package/src/utils/index.ts +45 -0
  360. package/src/utils/verifyAndroidBluetoothPermissions.ts +31 -0
@@ -0,0 +1,133 @@
1
+ import React from 'react';
2
+ import { StyleSheet, Text, View } from 'react-native';
3
+ import { UserInfoView } from './UserInfoView';
4
+ import { CallControlsButton } from './CallControlsButton';
5
+ import { Mic, MicOff, PhoneDown, Video, VideoSlash } from '../icons';
6
+ import { useStreamVideoStoreValue } from '../contexts/StreamVideoContext';
7
+ import { VideoRenderer } from './VideoRenderer';
8
+ import { useMutingState } from '../hooks/useMutingState';
9
+ import { useLocalVideoStream } from '../hooks/useLocalVideoStream';
10
+ import { theme } from '../theme';
11
+ import { useCall, useCallCallingState } from '@stream-io/video-react-bindings';
12
+ import { CallingState } from '@stream-io/video-client';
13
+
14
+ export const OutgoingCallView = () => {
15
+ const { isAudioMuted, isVideoMuted, toggleAudioState, toggleVideoState } =
16
+ useMutingState();
17
+ const call = useCall();
18
+ const callingState = useCallCallingState();
19
+
20
+ const hangupCallHandler = async () => {
21
+ try {
22
+ if (callingState === CallingState.LEFT) {
23
+ return;
24
+ }
25
+ await call?.leave();
26
+ } catch (error) {
27
+ console.log('Error leaving Call', error);
28
+ }
29
+ };
30
+ const muteStatusColor = (status: boolean) => {
31
+ return status ? theme.light.overlay_dark : theme.light.static_white;
32
+ };
33
+
34
+ return (
35
+ <>
36
+ <View style={[StyleSheet.absoluteFill, styles.container]}>
37
+ <View style={styles.content}>
38
+ <UserInfoView />
39
+ <Text style={styles.callingText}>Calling...</Text>
40
+ </View>
41
+ <View style={styles.buttonGroup}>
42
+ <View style={styles.deviceControlButtons}>
43
+ <CallControlsButton
44
+ onPress={toggleAudioState}
45
+ color={muteStatusColor(isAudioMuted)}
46
+ style={[styles.button, theme.button.lg]}
47
+ svgContainerStyle={[styles.svgContainerStyle, theme.icon.lg]}
48
+ >
49
+ {isAudioMuted ? (
50
+ <MicOff color={theme.light.static_white} />
51
+ ) : (
52
+ <Mic color={theme.light.static_black} />
53
+ )}
54
+ </CallControlsButton>
55
+ <CallControlsButton
56
+ onPress={toggleVideoState}
57
+ color={muteStatusColor(isVideoMuted)}
58
+ style={[styles.button, theme.button.lg]}
59
+ svgContainerStyle={[styles.svgContainerStyle, theme.icon.lg]}
60
+ >
61
+ {isVideoMuted ? (
62
+ <VideoSlash color={theme.light.static_white} />
63
+ ) : (
64
+ <Video color={theme.light.static_black} />
65
+ )}
66
+ </CallControlsButton>
67
+ </View>
68
+
69
+ <CallControlsButton
70
+ onPress={hangupCallHandler}
71
+ color={theme.light.error}
72
+ style={[styles.button, styles.hangupButton, theme.button.lg]}
73
+ svgContainerStyle={[styles.svgContainerStyle, theme.icon.lg]}
74
+ >
75
+ <PhoneDown color={theme.light.static_white} />
76
+ </CallControlsButton>
77
+ </View>
78
+ </View>
79
+ <Background />
80
+ </>
81
+ );
82
+ };
83
+
84
+ const Background = () => {
85
+ const localVideoStream = useLocalVideoStream();
86
+ const isVideoMuted = useStreamVideoStoreValue((store) => store.isVideoMuted);
87
+
88
+ if (isVideoMuted || !localVideoStream) {
89
+ return <View style={styles.background} />;
90
+ }
91
+ return (
92
+ <View style={styles.background}>
93
+ <VideoRenderer
94
+ mediaStream={localVideoStream}
95
+ zOrder={1}
96
+ style={StyleSheet.absoluteFill}
97
+ mirror
98
+ />
99
+ </View>
100
+ );
101
+ };
102
+
103
+ const styles = StyleSheet.create({
104
+ container: {
105
+ zIndex: 1,
106
+ display: 'flex',
107
+ flexDirection: 'column',
108
+ justifyContent: 'space-between',
109
+ paddingVertical: 2 * theme.margin.xl,
110
+ },
111
+ background: {
112
+ backgroundColor: theme.light.static_grey,
113
+ flex: 1,
114
+ },
115
+ content: {},
116
+ callingText: {
117
+ marginTop: theme.margin.md,
118
+ textAlign: 'center',
119
+ color: theme.light.static_white,
120
+ ...theme.fonts.heading6,
121
+ },
122
+ buttonGroup: {},
123
+ deviceControlButtons: {
124
+ flexDirection: 'row',
125
+ justifyContent: 'space-around',
126
+ marginBottom: theme.margin.md,
127
+ },
128
+ hangupButton: {
129
+ alignSelf: 'center',
130
+ },
131
+ button: {},
132
+ svgContainerStyle: {},
133
+ });
@@ -0,0 +1,59 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { StreamReaction } from '@stream-io/video-client';
3
+ import { StyleSheet, Text, View } from 'react-native';
4
+ import { useCall } from '@stream-io/video-react-bindings';
5
+ import { theme } from '../theme';
6
+ import { StreamVideoRN } from '../utils';
7
+
8
+ export type ReactionProps = {
9
+ reaction?: StreamReaction;
10
+ sessionId: string;
11
+ hideAfterTimeoutInMs?: number;
12
+ };
13
+
14
+ export const ParticipantReaction = (props: ReactionProps) => {
15
+ const { supportedReactions } = StreamVideoRN.config;
16
+ const { reaction, sessionId, hideAfterTimeoutInMs = 5500 } = props;
17
+ const call = useCall();
18
+ const [isShowing, setIsShowing] = useState<boolean>(false);
19
+
20
+ useEffect(() => {
21
+ let timeoutId: NodeJS.Timeout;
22
+ if (call) {
23
+ setIsShowing(true);
24
+ timeoutId = setTimeout(() => {
25
+ setIsShowing(false);
26
+ call.resetReaction(sessionId);
27
+ }, hideAfterTimeoutInMs);
28
+ }
29
+ return () => {
30
+ clearTimeout(timeoutId);
31
+ };
32
+ }, [call, hideAfterTimeoutInMs, sessionId, reaction]);
33
+
34
+ const currentReaction =
35
+ reaction &&
36
+ supportedReactions.find(
37
+ (supportedReaction) =>
38
+ supportedReaction.emoji_code === reaction.emoji_code,
39
+ );
40
+
41
+ let component;
42
+ if (isShowing) {
43
+ if (typeof currentReaction?.icon !== 'string')
44
+ component = currentReaction?.icon;
45
+ else
46
+ component = <Text style={styles.reaction}>{currentReaction.icon}</Text>;
47
+ }
48
+
49
+ return (
50
+ <View style={[styles.svgContainerStyle, theme.icon.md]}>{component}</View>
51
+ );
52
+ };
53
+
54
+ const styles = StyleSheet.create({
55
+ reaction: {
56
+ ...theme.fonts.heading6,
57
+ },
58
+ svgContainerStyle: {},
59
+ });
@@ -0,0 +1,315 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native';
3
+ import { MediaStream, RTCView } from 'react-native-webrtc';
4
+ import {
5
+ SfuModels,
6
+ StreamVideoLocalParticipant,
7
+ StreamVideoParticipant,
8
+ VisibilityState,
9
+ } from '@stream-io/video-client';
10
+ import { VideoRenderer } from './VideoRenderer';
11
+ import { Avatar } from './Avatar';
12
+ import { useStreamVideoStoreValue } from '../contexts';
13
+ import { MicOff, ScreenShare, VideoSlash } from '../icons';
14
+ import { theme } from '../theme';
15
+ import { palette } from '../theme/constants';
16
+ import { ParticipantReaction } from './ParticipantReaction';
17
+ import { useCall } from '@stream-io/video-react-bindings';
18
+ import { NetworkQualityIndicator } from './NetworkQualityIndicator';
19
+
20
+ /**
21
+ * Props to be passed for the ParticipantView component.
22
+ */
23
+ interface ParticipantViewProps {
24
+ /**
25
+ * The participant that will be displayed
26
+ */
27
+ participant: StreamVideoParticipant | StreamVideoLocalParticipant;
28
+ /**
29
+ * The video kind that will be displayed
30
+ */
31
+ kind: 'video' | 'screen';
32
+ /**
33
+ * Any custom style to be merged with the participant view
34
+ */
35
+ containerStyle?: StyleProp<ViewStyle>;
36
+ /**
37
+ * Any custom style to be merged with the VideoRenderer
38
+ */
39
+ videoRendererStyle?: StyleProp<ViewStyle>;
40
+ /**
41
+ * When set to false, the video stream will not be shown even if it is available.
42
+ */
43
+ isVisible?: boolean;
44
+ /**
45
+ * When set to true, the audio stream will not be played even if it is available.
46
+ */
47
+ disableAudio?: boolean;
48
+ }
49
+
50
+ /**
51
+ * Renders either the participants' video track or screenShare track
52
+ * and additional info, by an absence of a video track or when disableVideo is truthy,
53
+ * only an avatar and audio track will be rendered.
54
+ *
55
+ * | When Video is Enabled | When Video is Disabled |
56
+ * | :--- | :----: |
57
+ * |![participant-view-1](https://user-images.githubusercontent.com/25864161/217489213-d4532ca1-49ee-4ef5-940c-af2e55bc0a5f.png)|![participant-view-2](https://user-images.githubusercontent.com/25864161/217489207-fb20c124-8bce-4c2b-87f9-4fe67bc50438.png)|
58
+ */
59
+ export const ParticipantView = (props: ParticipantViewProps) => {
60
+ const { participant, kind, isVisible = true, disableAudio } = props;
61
+
62
+ const call = useCall();
63
+ const pendingVideoLayoutRef = useRef<SfuModels.VideoDimension>();
64
+ const subscribedVideoLayoutRef = useRef<SfuModels.VideoDimension>();
65
+ const { isSpeaking, isLoggedInUser, publishedTracks } = participant;
66
+ const isPublishingVideoTrack = publishedTracks.includes(
67
+ kind === 'video'
68
+ ? SfuModels.TrackType.VIDEO
69
+ : SfuModels.TrackType.SCREEN_SHARE,
70
+ );
71
+ const isCameraOnFrontFacingMode = useStreamVideoStoreValue(
72
+ (store) => store.isCameraOnFrontFacingMode,
73
+ );
74
+ const { connectionQuality, reaction, sessionId } = participant;
75
+
76
+ /**
77
+ * This effect updates the participant's viewportVisibilityState
78
+ * Additionally makes sure that when this view becomes visible again, the layout to subscribe is known
79
+ */
80
+ useEffect(() => {
81
+ if (!call) return;
82
+ if (isVisible) {
83
+ if (participant.viewportVisibilityState !== VisibilityState.VISIBLE) {
84
+ call.state.updateParticipant(participant.sessionId, (p) => ({
85
+ ...p,
86
+ viewportVisibilityState: VisibilityState.VISIBLE,
87
+ }));
88
+ }
89
+ } else {
90
+ if (participant.viewportVisibilityState !== VisibilityState.INVISIBLE) {
91
+ call.state.updateParticipant(participant.sessionId, (p) => ({
92
+ ...p,
93
+ viewportVisibilityState: VisibilityState.INVISIBLE,
94
+ }));
95
+ }
96
+ if (subscribedVideoLayoutRef.current) {
97
+ // when video is enabled again, we want to use the last subscribed dimension to resubscribe
98
+ pendingVideoLayoutRef.current = subscribedVideoLayoutRef.current;
99
+ subscribedVideoLayoutRef.current = undefined;
100
+ }
101
+ }
102
+ }, [
103
+ participant.sessionId,
104
+ participant.viewportVisibilityState,
105
+ isVisible,
106
+ call,
107
+ ]);
108
+
109
+ /**
110
+ * This effect updates the subscription either
111
+ * 1. when video tracks are published and was unpublished before
112
+ * 2. when the view's visibility changes
113
+ */
114
+ useEffect(() => {
115
+ // NOTE: We only want to update the subscription if the pendingVideoLayoutRef is set
116
+ const updateIsNeeded = pendingVideoLayoutRef.current;
117
+ if (!updateIsNeeded || !call || !isPublishingVideoTrack) return;
118
+
119
+ // NOTE: When the view is not visible, we want to subscribe to audio only.
120
+ // We unsubscribe their video by setting the dimension to undefined
121
+ const dimension = isVisible ? pendingVideoLayoutRef.current : undefined;
122
+
123
+ call.updateSubscriptionsPartial(kind, {
124
+ [participant.sessionId]: { dimension },
125
+ });
126
+
127
+ if (dimension) {
128
+ subscribedVideoLayoutRef.current = pendingVideoLayoutRef.current;
129
+ pendingVideoLayoutRef.current = undefined;
130
+ }
131
+ }, [call, isPublishingVideoTrack, kind, participant.sessionId, isVisible]);
132
+
133
+ useEffect(() => {
134
+ return () => {
135
+ subscribedVideoLayoutRef.current = undefined;
136
+ pendingVideoLayoutRef.current = undefined;
137
+ };
138
+ }, [kind, participant.sessionId]);
139
+
140
+ const onLayout: React.ComponentProps<typeof View>['onLayout'] = (event) => {
141
+ const dimension = {
142
+ width: Math.trunc(event.nativeEvent.layout.width),
143
+ height: Math.trunc(event.nativeEvent.layout.height),
144
+ };
145
+
146
+ // NOTE: If the participant hasn't published a video track yet,
147
+ // or the view is not viewable, we store the dimensions and handle it
148
+ // when the track is published or the video is enabled.
149
+ if (!call || !isPublishingVideoTrack || !isVisible) {
150
+ pendingVideoLayoutRef.current = dimension;
151
+ return;
152
+ }
153
+
154
+ // NOTE: We don't want to update the subscription if the dimension hasn't changed
155
+ if (
156
+ subscribedVideoLayoutRef.current?.width === dimension.width &&
157
+ subscribedVideoLayoutRef.current?.height === dimension.height
158
+ ) {
159
+ return;
160
+ }
161
+
162
+ call.updateSubscriptionsPartial(kind, {
163
+ [participant.sessionId]: {
164
+ dimension,
165
+ },
166
+ });
167
+ subscribedVideoLayoutRef.current = dimension;
168
+ pendingVideoLayoutRef.current = undefined;
169
+ };
170
+
171
+ // NOTE: We have to cast to MediaStream type from webrtc
172
+ // as JS client sends the web navigators' mediastream type instead
173
+ const videoStream = (
174
+ kind === 'video' ? participant.videoStream : participant.screenShareStream
175
+ ) as MediaStream | undefined;
176
+
177
+ const audioStream = participant.audioStream as MediaStream | undefined;
178
+ const isAudioMuted = !publishedTracks.includes(SfuModels.TrackType.AUDIO);
179
+ const isVideoMuted = !publishedTracks.includes(SfuModels.TrackType.VIDEO);
180
+ const hasScreenShareTrack = publishedTracks.includes(
181
+ SfuModels.TrackType.SCREEN_SHARE,
182
+ );
183
+ const isScreenSharing = kind === 'screen';
184
+ const hasVideoTrack = isScreenSharing ? hasScreenShareTrack : !isVideoMuted;
185
+ const mirror = isLoggedInUser && isCameraOnFrontFacingMode;
186
+ const isAudioAvailable =
187
+ kind === 'video' && !!audioStream && !isAudioMuted && !disableAudio;
188
+ const canShowVideo = !!videoStream && isVisible && hasVideoTrack;
189
+ const applySpeakerStyle = isSpeaking && !isScreenSharing;
190
+ const speakerStyle = applySpeakerStyle && styles.isSpeaking;
191
+ const videoOnlyStyle = {
192
+ borderColor: palette.grey800,
193
+ borderWidth: 2,
194
+ };
195
+
196
+ const participantLabel = participant.userId;
197
+
198
+ return (
199
+ <View
200
+ style={[
201
+ styles.containerBase,
202
+ videoOnlyStyle,
203
+ props.containerStyle,
204
+ speakerStyle,
205
+ ]}
206
+ accessibilityLabel={`participant-${participant.userId}`}
207
+ accessibilityValue={{
208
+ text: isSpeaking
209
+ ? 'participant-is-speaking'
210
+ : 'participant-is-not-speaking',
211
+ }}
212
+ onLayout={onLayout}
213
+ >
214
+ <View style={styles.topView}>
215
+ <ParticipantReaction reaction={reaction} sessionId={sessionId} />
216
+ </View>
217
+ {canShowVideo ? (
218
+ <VideoRenderer
219
+ zOrder={1}
220
+ mirror={mirror}
221
+ mediaStream={videoStream as MediaStream}
222
+ objectFit={isScreenSharing ? 'contain' : 'cover'}
223
+ style={[styles.videoRenderer, props.videoRendererStyle]}
224
+ />
225
+ ) : (
226
+ <Avatar participant={participant} />
227
+ )}
228
+ {isAudioAvailable && (
229
+ <RTCView streamURL={(audioStream as MediaStream).toURL()} />
230
+ )}
231
+ <View style={styles.bottomView}>
232
+ {kind === 'video' && (
233
+ <View style={styles.status}>
234
+ <Text style={styles.userNameLabel} numberOfLines={1}>
235
+ {participantLabel}
236
+ </Text>
237
+ {isAudioMuted && (
238
+ <View style={[styles.svgContainerStyle, theme.icon.xs]}>
239
+ <MicOff color={theme.light.error} />
240
+ </View>
241
+ )}
242
+ {isVideoMuted && (
243
+ <View style={[styles.svgContainerStyle, theme.icon.xs]}>
244
+ <VideoSlash color={theme.light.error} />
245
+ </View>
246
+ )}
247
+ </View>
248
+ )}
249
+ {kind === 'screen' && (
250
+ <View style={styles.screenViewStatus}>
251
+ <View style={[{ marginRight: theme.margin.sm }, theme.icon.md]}>
252
+ <ScreenShare color={theme.light.static_white} />
253
+ </View>
254
+ <Text style={styles.userNameLabel} numberOfLines={1}>
255
+ {participant.userId} is sharing their screen.
256
+ </Text>
257
+ </View>
258
+ )}
259
+ <NetworkQualityIndicator connectionQuality={connectionQuality} />
260
+ </View>
261
+ </View>
262
+ );
263
+ };
264
+
265
+ const styles = StyleSheet.create({
266
+ containerBase: {
267
+ alignItems: 'center',
268
+ justifyContent: 'space-between',
269
+ padding: theme.padding.xs,
270
+ },
271
+ topView: {
272
+ alignSelf: 'flex-end',
273
+ zIndex: 10,
274
+ },
275
+ videoRenderer: {
276
+ ...StyleSheet.absoluteFillObject,
277
+ },
278
+ bottomView: {
279
+ alignSelf: 'stretch',
280
+ display: 'flex',
281
+ flexDirection: 'row',
282
+ alignItems: 'center',
283
+ justifyContent: 'space-between',
284
+ },
285
+ status: {
286
+ display: 'flex',
287
+ flexDirection: 'row',
288
+ padding: theme.padding.sm,
289
+ borderRadius: theme.rounded.xs,
290
+ backgroundColor: theme.light.static_overlay,
291
+ flexShrink: 1,
292
+ marginRight: theme.margin.sm,
293
+ },
294
+ screenViewStatus: {
295
+ padding: theme.padding.sm,
296
+ borderRadius: theme.rounded.xs,
297
+ backgroundColor: theme.light.static_overlay,
298
+ flexDirection: 'row',
299
+ alignItems: 'center',
300
+ flexShrink: 1,
301
+ marginRight: theme.margin.sm,
302
+ },
303
+ userNameLabel: {
304
+ flexShrink: 1,
305
+ color: theme.light.static_white,
306
+ ...theme.fonts.caption,
307
+ },
308
+ svgContainerStyle: {
309
+ marginLeft: theme.margin.xs,
310
+ },
311
+ isSpeaking: {
312
+ borderColor: theme.light.primary,
313
+ borderWidth: 2,
314
+ },
315
+ });
@@ -0,0 +1,90 @@
1
+ import { StreamReaction } from '@stream-io/video-client';
2
+ import { Pressable, StyleSheet, View, Text, Modal } from 'react-native';
3
+
4
+ import { theme } from '../theme';
5
+ import { useCallback } from 'react';
6
+ import { useCall } from '@stream-io/video-react-bindings';
7
+ import { StreamVideoRN } from '../utils';
8
+ import { Cross } from '../icons';
9
+
10
+ type ReactionModalType = {
11
+ isReactionModalActive: boolean;
12
+ setIsReactionModalActive: React.Dispatch<React.SetStateAction<boolean>>;
13
+ };
14
+
15
+ export const ReactionModal = (props: ReactionModalType) => {
16
+ const { isReactionModalActive, setIsReactionModalActive } = props;
17
+ const onCloseReactionsModal = useCallback(() => {
18
+ setIsReactionModalActive(false);
19
+ }, [setIsReactionModalActive]);
20
+ const call = useCall();
21
+ const { supportedReactions } = StreamVideoRN.config;
22
+
23
+ const sendReaction = async (reaction: StreamReaction) => {
24
+ await call?.sendReaction(reaction);
25
+ setIsReactionModalActive(false);
26
+ };
27
+
28
+ return (
29
+ <Modal
30
+ animationType="slide"
31
+ transparent={true}
32
+ visible={isReactionModalActive}
33
+ onRequestClose={onCloseReactionsModal}
34
+ >
35
+ <View style={styles.container}>
36
+ <View style={styles.menu}>
37
+ <View style={styles.reactions}>
38
+ {supportedReactions.map((reaction) => (
39
+ <Pressable
40
+ onPress={() => sendReaction(reaction)}
41
+ key={reaction.emoji_code}
42
+ style={styles.reaction}
43
+ >
44
+ <Text>
45
+ {reaction.emoji_code &&
46
+ supportedReactions.find(
47
+ (supportedReaction) =>
48
+ supportedReaction.emoji_code === reaction.emoji_code,
49
+ )?.icon}
50
+ </Text>
51
+ </Pressable>
52
+ ))}
53
+ <Pressable
54
+ style={[styles.closeIcon, theme.icon.sm]}
55
+ onPress={onCloseReactionsModal}
56
+ >
57
+ <Cross color={theme.light.primary} />
58
+ </Pressable>
59
+ </View>
60
+ </View>
61
+ </View>
62
+ </Modal>
63
+ );
64
+ };
65
+
66
+ const styles = StyleSheet.create({
67
+ container: {
68
+ flex: 1,
69
+ justifyContent: 'center',
70
+ alignItems: 'center',
71
+ },
72
+ menu: {
73
+ backgroundColor: theme.light.bars,
74
+ borderRadius: theme.rounded.md,
75
+ },
76
+ reactions: {
77
+ display: 'flex',
78
+ flexDirection: 'row',
79
+ alignItems: 'center',
80
+ justifyContent: 'space-between',
81
+ padding: theme.padding.md,
82
+ },
83
+ reaction: {
84
+ marginHorizontal: theme.margin.md,
85
+ },
86
+ svgContainerStyle: {},
87
+ closeIcon: {
88
+ marginLeft: theme.margin.md,
89
+ },
90
+ });
@@ -0,0 +1,103 @@
1
+ import { AxiosError, OwnCapability } from '@stream-io/video-client';
2
+ import {
3
+ Restricted,
4
+ useCall,
5
+ useHasPermissions,
6
+ } from '@stream-io/video-react-bindings';
7
+ import { CallControlsButton } from './CallControlsButton';
8
+ import { useCallControls, usePermissionNotification } from '../hooks';
9
+ import { theme } from '../theme';
10
+ import { Mic, MicOff } from '../icons';
11
+ import { Alert, StyleSheet } from 'react-native';
12
+ import { muteStatusColor } from '../utils';
13
+ import { useCallback, useEffect, useState } from 'react';
14
+
15
+ export const ToggleAudioButton = () => {
16
+ const [isAwaitingApproval, setIsAwaitingApproval] = useState(false);
17
+ const { toggleAudioMuted, isAudioPublished } = useCallControls();
18
+ const userHasSendAudioCapability = useHasPermissions(
19
+ OwnCapability.SEND_AUDIO,
20
+ );
21
+
22
+ const isAudioMuted = !isAudioPublished;
23
+
24
+ usePermissionNotification({
25
+ permission: OwnCapability.SEND_AUDIO,
26
+ messageApproved: 'You can now speak.',
27
+ messageRevoked: 'You can no longer speak.',
28
+ });
29
+
30
+ const call = useCall();
31
+
32
+ useEffect(() => {
33
+ if (userHasSendAudioCapability) {
34
+ setIsAwaitingApproval(false);
35
+ }
36
+ }, [userHasSendAudioCapability]);
37
+
38
+ const handleRequestPermission = useCallback(
39
+ async (permission: OwnCapability) => {
40
+ if (!call?.permissionsContext.canRequest(permission)) {
41
+ return;
42
+ }
43
+ setIsAwaitingApproval(true);
44
+ try {
45
+ await call.requestPermissions({ permissions: [permission] });
46
+ } catch (error) {
47
+ if (error instanceof AxiosError) {
48
+ console.log(
49
+ 'RequestPermissions failed',
50
+ error.response?.data.message,
51
+ );
52
+ }
53
+ }
54
+ },
55
+ [call],
56
+ );
57
+
58
+ const handleToggleAudioButton = async () => {
59
+ if (userHasSendAudioCapability) {
60
+ await toggleAudioMuted();
61
+ return;
62
+ }
63
+ if (!isAwaitingApproval) {
64
+ await handleRequestPermission(OwnCapability.SEND_AUDIO);
65
+ } else {
66
+ Alert.alert('Awaiting for an approval to speak.');
67
+ }
68
+ };
69
+
70
+ return (
71
+ <Restricted requiredGrants={[OwnCapability.SEND_AUDIO]}>
72
+ <CallControlsButton
73
+ onPress={handleToggleAudioButton}
74
+ color={muteStatusColor(isAudioMuted)}
75
+ style={!isAudioMuted ? styles.button : null}
76
+ >
77
+ {isAudioMuted ? (
78
+ <MicOff color={theme.light.static_white} />
79
+ ) : (
80
+ <Mic color={theme.light.static_black} />
81
+ )}
82
+ </CallControlsButton>
83
+ </Restricted>
84
+ );
85
+ };
86
+
87
+ const styles = StyleSheet.create({
88
+ button: {
89
+ // For iOS
90
+ shadowOffset: {
91
+ width: 0,
92
+ height: 6,
93
+ },
94
+ shadowOpacity: 0.37,
95
+ shadowRadius: 7.49,
96
+
97
+ // For android
98
+ elevation: 6,
99
+ },
100
+ svgContainerStyle: {
101
+ paddingTop: theme.padding.xs,
102
+ },
103
+ });