@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,287 @@
1
+ import {
2
+ OwnCapability,
3
+ SfuModels,
4
+ StreamVideoParticipant,
5
+ } from '@stream-io/video-client';
6
+ import {
7
+ Restricted,
8
+ useCall,
9
+ useConnectedUser,
10
+ useHasPermissions,
11
+ useParticipantCount,
12
+ useParticipants,
13
+ } from '@stream-io/video-react-bindings';
14
+ import {
15
+ Alert,
16
+ FlatList,
17
+ Modal,
18
+ Pressable,
19
+ StyleSheet,
20
+ Text,
21
+ View,
22
+ } from 'react-native';
23
+ import { ArrowRight, Cross, MicOff, ScreenShare, VideoSlash } from '../icons';
24
+ import React, { useCallback, useState } from 'react';
25
+ import { generateParticipantTitle } from '../utils';
26
+ import { CallParticipantOptions } from './CallParticipantsOptions';
27
+ import { Avatar } from './Avatar';
28
+ import { theme } from '../theme';
29
+
30
+ type CallParticipantInfoViewType = {
31
+ participant: StreamVideoParticipant;
32
+ setSelectedParticipant: React.Dispatch<
33
+ React.SetStateAction<StreamVideoParticipant | undefined>
34
+ >;
35
+ };
36
+
37
+ const CallParticipantInfoItem = (props: CallParticipantInfoViewType) => {
38
+ const { participant, setSelectedParticipant } = props;
39
+ const connectedUser = useConnectedUser();
40
+ const participantIsLoggedInUser = participant.userId === connectedUser?.id;
41
+ const userHasMuteUsersCapability = useHasPermissions(
42
+ OwnCapability.MUTE_USERS,
43
+ );
44
+ const userHasUpdateCallPermissionsCapability = useHasPermissions(
45
+ OwnCapability.UPDATE_CALL_PERMISSIONS,
46
+ );
47
+ const userHasBlockUserCapability = useHasPermissions(
48
+ OwnCapability.BLOCK_USERS,
49
+ );
50
+ const optionsOpenHandler = useCallback(() => {
51
+ if (!participantIsLoggedInUser) setSelectedParticipant(participant);
52
+ }, [participant, setSelectedParticipant, participantIsLoggedInUser]);
53
+
54
+ if (!participant) return null;
55
+ const { publishedTracks } = participant;
56
+ const isAudioMuted = !publishedTracks.includes(SfuModels.TrackType.AUDIO);
57
+ const isVideoMuted = !publishedTracks.includes(SfuModels.TrackType.VIDEO);
58
+ const isScreenSharing = publishedTracks.includes(
59
+ SfuModels.TrackType.SCREEN_SHARE,
60
+ );
61
+ const isParticipantItemPressable =
62
+ userHasBlockUserCapability ||
63
+ userHasMuteUsersCapability ||
64
+ userHasUpdateCallPermissionsCapability;
65
+
66
+ return (
67
+ <Pressable
68
+ style={styles.participant}
69
+ onPress={optionsOpenHandler}
70
+ disabled={!isParticipantItemPressable}
71
+ >
72
+ <Avatar radius={theme.avatar.xs} participant={participant} />
73
+
74
+ <Text style={styles.name}>
75
+ {(participant.name || generateParticipantTitle(participant.userId)) +
76
+ (participantIsLoggedInUser ? ' (You)' : '')}
77
+ </Text>
78
+ <View style={styles.icons}>
79
+ {isScreenSharing && (
80
+ <View style={[styles.svgContainerStyle, theme.icon.md]}>
81
+ <ScreenShare color={theme.light.info} />
82
+ </View>
83
+ )}
84
+ {isAudioMuted && (
85
+ <View style={[styles.svgContainerStyle, theme.icon.sm]}>
86
+ <MicOff color={theme.light.error} />
87
+ </View>
88
+ )}
89
+ {isVideoMuted && (
90
+ <View style={[styles.svgContainerStyle, theme.icon.sm]}>
91
+ <VideoSlash color={theme.light.error} />
92
+ </View>
93
+ )}
94
+ {!participantIsLoggedInUser && (
95
+ <Restricted
96
+ requiredGrants={[
97
+ OwnCapability.MUTE_USERS,
98
+ OwnCapability.UPDATE_CALL_PERMISSIONS,
99
+ OwnCapability.BLOCK_USERS,
100
+ ]}
101
+ >
102
+ <View style={[styles.svgContainerStyle, theme.icon.sm]}>
103
+ <ArrowRight color={theme.light.text_high_emphasis} />
104
+ </View>
105
+ </Restricted>
106
+ )}
107
+ </View>
108
+ </Pressable>
109
+ );
110
+ };
111
+
112
+ export interface CallParticipantsInfoViewType {
113
+ /**
114
+ * Boolean that decided whether the CallPartcipantsInfoView modal should be open or not.
115
+ */
116
+ isCallParticipantsViewVisible: boolean;
117
+ /**
118
+ * SetState function to set the value of the boolean field `isCallParticipantsViewVisible` depending upon whether the CallPartcipantsInfoView modal should be open or not.
119
+ */
120
+ setIsCallParticipantsViewVisible: React.Dispatch<
121
+ React.SetStateAction<boolean>
122
+ >;
123
+ }
124
+
125
+ /**
126
+ * Shows information about the call, it's participants in the call and
127
+ * their mute states, handler to trigger options (TBD, permissions not impl)
128
+ * and options to invite more people to the call.
129
+ *
130
+ * | Participants List | Options Modal is Open |
131
+ * | :--- | :----: |
132
+ * |![call-participants-info-view-1](https://user-images.githubusercontent.com/25864161/217341952-1e875bc3-e31f-42eb-918b-307eace116b1.png) | ![call-participants-info-view-2](https://user-images.githubusercontent.com/25864161/217341960-5016b678-d1a5-4ecf-bb4b-e463987b9cae.png)|
133
+ **/
134
+ export const CallParticipantsInfoView = ({
135
+ isCallParticipantsViewVisible,
136
+ setIsCallParticipantsViewVisible,
137
+ }: CallParticipantsInfoViewType) => {
138
+ const participants = useParticipants();
139
+ const participantCount = useParticipantCount();
140
+ const [selectedParticipant, setSelectedParticipant] = useState<
141
+ StreamVideoParticipant | undefined
142
+ >(undefined);
143
+ const call = useCall();
144
+
145
+ const muteAllParticipantsHandler = async () => {
146
+ try {
147
+ await call?.muteAllUsers('audio');
148
+ Alert.alert('Users Muted Successfully');
149
+ } catch (error) {
150
+ console.log('Error muting users', error);
151
+ }
152
+ };
153
+
154
+ const onCloseCallParticipantsViewVisible = () => {
155
+ setIsCallParticipantsViewVisible(false);
156
+ };
157
+
158
+ const renderItem = useCallback(
159
+ ({ item }: { item: StreamVideoParticipant }) => {
160
+ return (
161
+ <CallParticipantInfoItem
162
+ key={item.sessionId}
163
+ participant={item}
164
+ setSelectedParticipant={setSelectedParticipant}
165
+ />
166
+ );
167
+ },
168
+ [],
169
+ );
170
+
171
+ return (
172
+ <Modal
173
+ animationType="slide"
174
+ transparent={true}
175
+ visible={isCallParticipantsViewVisible}
176
+ onRequestClose={onCloseCallParticipantsViewVisible}
177
+ >
178
+ <View style={styles.container}>
179
+ <View style={styles.content}>
180
+ <View style={styles.header}>
181
+ <View style={styles.leftHeaderElement} />
182
+ <Text style={styles.headerText}>
183
+ Participants ({participantCount})
184
+ </Text>
185
+ <Pressable
186
+ style={[styles.closeIcon, theme.icon.sm]}
187
+ onPress={onCloseCallParticipantsViewVisible}
188
+ >
189
+ <Cross color={theme.light.primary} />
190
+ </Pressable>
191
+ </View>
192
+ <View style={styles.buttonGroup}>
193
+ <Restricted requiredGrants={[OwnCapability.MUTE_USERS]}>
194
+ <Pressable
195
+ style={styles.button}
196
+ onPress={muteAllParticipantsHandler}
197
+ >
198
+ <Text style={styles.buttonText}>Mute All</Text>
199
+ </Pressable>
200
+ </Restricted>
201
+ </View>
202
+ <FlatList data={participants} renderItem={renderItem} />
203
+ {selectedParticipant && (
204
+ <View style={[StyleSheet.absoluteFill, styles.modal]}>
205
+ <CallParticipantOptions
206
+ participant={selectedParticipant}
207
+ setSelectedParticipant={setSelectedParticipant}
208
+ />
209
+ </View>
210
+ )}
211
+ </View>
212
+ </View>
213
+ </Modal>
214
+ );
215
+ };
216
+
217
+ const styles = StyleSheet.create({
218
+ container: {
219
+ flex: 1,
220
+ justifyContent: 'center',
221
+ },
222
+ content: {
223
+ flex: 1,
224
+ backgroundColor: theme.light.bars,
225
+ borderRadius: theme.rounded.md,
226
+ marginVertical: theme.margin.lg,
227
+ marginHorizontal: theme.margin.md,
228
+ },
229
+ header: {
230
+ display: 'flex',
231
+ flexDirection: 'row',
232
+ justifyContent: 'space-between',
233
+ alignItems: 'center',
234
+ paddingVertical: theme.padding.md,
235
+ width: '100%',
236
+ },
237
+ leftHeaderElement: {
238
+ marginLeft: theme.margin.md,
239
+ },
240
+ headerText: {
241
+ ...theme.fonts.bodyBold,
242
+ color: theme.light.text_high_emphasis,
243
+ },
244
+ closeIcon: {
245
+ marginRight: theme.margin.md,
246
+ },
247
+ buttonGroup: {},
248
+ button: {
249
+ backgroundColor: theme.light.primary,
250
+ borderRadius: theme.rounded.lg,
251
+ padding: theme.padding.md,
252
+ margin: theme.margin.lg,
253
+ },
254
+ buttonText: {
255
+ textAlign: 'center',
256
+ color: theme.light.static_white,
257
+ ...theme.fonts.subtitleBold,
258
+ },
259
+ participant: {
260
+ paddingHorizontal: theme.padding.sm,
261
+ paddingVertical: theme.padding.xs,
262
+ display: 'flex',
263
+ flexDirection: 'row',
264
+ alignItems: 'center',
265
+ borderBottomColor: theme.light.borders,
266
+ borderBottomWidth: 1,
267
+ },
268
+ name: {
269
+ marginLeft: theme.margin.sm,
270
+ color: theme.light.text_high_emphasis,
271
+ ...theme.fonts.subtitleBold,
272
+ },
273
+ icons: {
274
+ position: 'absolute',
275
+ right: theme.spacing.lg,
276
+ display: 'flex',
277
+ flexDirection: 'row',
278
+ },
279
+ svgContainerStyle: {
280
+ marginLeft: theme.margin.sm,
281
+ },
282
+ modal: {
283
+ alignItems: 'center',
284
+ justifyContent: 'center',
285
+ backgroundColor: theme.light.overlay,
286
+ },
287
+ });
@@ -0,0 +1,172 @@
1
+ import React, {
2
+ useCallback,
3
+ useMemo,
4
+ useReducer,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import { FlatList, StyleProp, StyleSheet, ViewStyle } from 'react-native';
9
+ import { ParticipantView } from './ParticipantView';
10
+ import {
11
+ StreamVideoLocalParticipant,
12
+ StreamVideoParticipant,
13
+ StreamVideoParticipantPatches,
14
+ VisibilityState,
15
+ } from '@stream-io/video-client';
16
+ import { theme } from '../theme';
17
+ import { useDebouncedValue } from '../utils/hooks/useDebouncedValue';
18
+ import { useCall } from '@stream-io/video-react-bindings';
19
+ import { A11yComponents } from '../constants/A11yLabels';
20
+
21
+ type FlatListProps = React.ComponentProps<
22
+ typeof FlatList<StreamVideoParticipant | StreamVideoLocalParticipant>
23
+ >;
24
+
25
+ const VIEWABILITY_CONFIG: FlatListProps['viewabilityConfig'] = {
26
+ waitForInteraction: false,
27
+ itemVisiblePercentThreshold: 60,
28
+ };
29
+
30
+ /**
31
+ * The props for the CallParticipantsList component
32
+ */
33
+ interface CallParticipantsListProps {
34
+ /**
35
+ * The list of participants to display in the list
36
+ */
37
+ participants: (StreamVideoParticipant | StreamVideoLocalParticipant)[];
38
+ /**
39
+ * The number of columns to display in the list of participants while in vertical or horizontal scrolling mode
40
+ * @default 2
41
+ */
42
+ numColumns?: number;
43
+ /**
44
+ * If true, the list will be displayed in horizontal scrolling mode
45
+ */
46
+ horizontal?: boolean;
47
+ }
48
+
49
+ /**
50
+ * The CallParticipantsList component displays a list of participants in a FlatList
51
+ * NOTE: this component depends on a flex container to calculate the width and height of the participant view, hence it should be used only in a flex parent container
52
+ */
53
+ export const CallParticipantsList = (props: CallParticipantsListProps) => {
54
+ const { numColumns = 2, horizontal, participants } = props;
55
+ const [containerWidth, setContainerWidth] = useState(0);
56
+
57
+ // we use a HashSet to track the currently viewable participants
58
+ // and a separate force update state to rerender the component to inform that the HashSet has changed
59
+ // NOTE: we use set instead of array or object for O(1) lookup, add and delete
60
+ const viewableParticipantSessionIds = useRef<Set<string>>(new Set());
61
+ const [_forceUpdateValue, forceUpdate] = useReducer((x) => x + 1, 0);
62
+ const forceUpdateValue = useDebouncedValue(_forceUpdateValue, 500); // we debounce forced value to avoid multiple viewability change continuous rerenders due to callbacks that occurs simultaneously during a large list scroll or when scrolling is completed
63
+
64
+ // we use a ref to store the active call object
65
+ // so that it can be used in the onViewableItemsChanged callback
66
+ const activeCall = useCall();
67
+ const activeCallRef = useRef(activeCall);
68
+ activeCallRef.current = activeCall;
69
+ // This is the function that gets called when the user scrolls the list of participants.
70
+ // It updates viewableParticipantSessionIds HashSet with the session IDs
71
+ // of the participants that are currently visible.
72
+ const onViewableItemsChanged = useRef<
73
+ FlatListProps['onViewableItemsChanged']
74
+ >(({ viewableItems }) => {
75
+ const participantPatches: StreamVideoParticipantPatches = {};
76
+ let mustUpdate = false;
77
+ const newVisibleParticipantSessionIds = new Set<string>(
78
+ viewableItems.map((v) => v.key),
79
+ );
80
+ const oldVisibleParticipantSessionIds =
81
+ viewableParticipantSessionIds.current;
82
+ newVisibleParticipantSessionIds.forEach((key) => {
83
+ if (!oldVisibleParticipantSessionIds.has(key)) {
84
+ mustUpdate = true;
85
+ participantPatches[key] = {
86
+ viewportVisibilityState: VisibilityState.VISIBLE,
87
+ };
88
+ }
89
+ });
90
+ oldVisibleParticipantSessionIds.forEach((key) => {
91
+ if (!newVisibleParticipantSessionIds.has(key)) {
92
+ mustUpdate = true;
93
+ participantPatches[key] = {
94
+ viewportVisibilityState: VisibilityState.INVISIBLE,
95
+ };
96
+ }
97
+ });
98
+ viewableParticipantSessionIds.current = newVisibleParticipantSessionIds;
99
+ if (mustUpdate) {
100
+ activeCallRef.current?.state.updateParticipants(participantPatches);
101
+ forceUpdate();
102
+ }
103
+ }).current;
104
+
105
+ // NOTE: key must be sessionId always as it is used to track viewable participants
106
+ const keyExtractor = useRef<FlatListProps['keyExtractor']>(
107
+ (item) => item.sessionId,
108
+ ).current;
109
+
110
+ const onLayout = useRef<FlatListProps['onLayout']>((event) => {
111
+ const { width } = event.nativeEvent.layout;
112
+ setContainerWidth(width);
113
+ }).current;
114
+
115
+ const itemContainerStyle = useMemo<StyleProp<ViewStyle>>(() => {
116
+ // we calculate the size of the participant view based on the containerWidth (the phone's screen width),
117
+ // number of columns and the margin between the views
118
+ const size = containerWidth / numColumns - theme.margin.sm * 2;
119
+ const style = { width: size, height: size };
120
+ if (horizontal) {
121
+ return [styles.participantWrapperHorizontal, style];
122
+ }
123
+ return [styles.participantWrapperVertical, style];
124
+ }, [horizontal, numColumns, containerWidth]);
125
+
126
+ const renderItem = useCallback<NonNullable<FlatListProps['renderItem']>>(
127
+ ({ item: participant }) => {
128
+ const isVisible = viewableParticipantSessionIds.current.has(
129
+ participant.sessionId,
130
+ );
131
+ return (
132
+ <ParticipantView
133
+ participant={participant}
134
+ containerStyle={itemContainerStyle}
135
+ kind="video"
136
+ isVisible={isVisible}
137
+ />
138
+ );
139
+ },
140
+ [itemContainerStyle],
141
+ );
142
+
143
+ return (
144
+ <FlatList
145
+ onLayout={onLayout}
146
+ key={!horizontal ? numColumns : undefined} // setting numColumns as key is a strict requirement of react-native to support changing numColumns on the fly
147
+ data={participants}
148
+ keyExtractor={keyExtractor}
149
+ viewabilityConfig={VIEWABILITY_CONFIG}
150
+ onViewableItemsChanged={onViewableItemsChanged}
151
+ renderItem={renderItem}
152
+ numColumns={!horizontal ? numColumns : undefined}
153
+ horizontal={horizontal}
154
+ showsHorizontalScrollIndicator={false}
155
+ extraData={`${forceUpdateValue}${containerWidth}`} // this is important to force re-render when visibility changes
156
+ accessibilityLabel={A11yComponents.CALL_PARTICIPANTS_LIST}
157
+ />
158
+ );
159
+ };
160
+
161
+ const styles = StyleSheet.create({
162
+ participantWrapperVertical: {
163
+ margin: theme.margin.sm,
164
+ overflow: 'hidden',
165
+ borderRadius: theme.rounded.sm,
166
+ },
167
+ participantWrapperHorizontal: {
168
+ marginHorizontal: theme.margin.sm,
169
+ overflow: 'hidden',
170
+ borderRadius: theme.rounded.sm,
171
+ },
172
+ });
@@ -0,0 +1,248 @@
1
+ import {
2
+ OwnCapability,
3
+ SfuModels,
4
+ StreamVideoParticipant,
5
+ } from '@stream-io/video-client';
6
+ import { Cross, VideoDisabled } from '../icons';
7
+ import { Pressable, StyleSheet, Text, View } from 'react-native';
8
+ import { generateParticipantTitle } from '../utils';
9
+ import { useCallback } from 'react';
10
+ import { Avatar } from './Avatar';
11
+ import { theme } from '../theme';
12
+ import { useCall, useHasPermissions } from '@stream-io/video-react-bindings';
13
+
14
+ type CallParticipantOptionType = {
15
+ title: string;
16
+ icon?: JSX.Element;
17
+ onPressHandler: () => void;
18
+ };
19
+
20
+ type CallParticipantOptionsType = {
21
+ participant: StreamVideoParticipant;
22
+ setSelectedParticipant: React.Dispatch<
23
+ React.SetStateAction<StreamVideoParticipant | undefined>
24
+ >;
25
+ };
26
+
27
+ export const CallParticipantOptions = (props: CallParticipantOptionsType) => {
28
+ const { participant, setSelectedParticipant } = props;
29
+ const call = useCall();
30
+
31
+ const grantPermission = async (permission: string) => {
32
+ await call?.updateUserPermissions({
33
+ user_id: participant.userId,
34
+ grant_permissions: [permission],
35
+ });
36
+ };
37
+
38
+ const revokePermission = async (permission: string) => {
39
+ await call?.updateUserPermissions({
40
+ user_id: participant.userId,
41
+ revoke_permissions: [permission],
42
+ });
43
+ };
44
+
45
+ const muteUser = async (mediaType: 'audio' | 'video') => {
46
+ await call?.muteUser(participant.userId, mediaType);
47
+ };
48
+
49
+ const muteUserAudio = async () => {
50
+ await muteUser('audio');
51
+ };
52
+
53
+ const muteUserVideo = async () => {
54
+ await muteUser('video');
55
+ };
56
+
57
+ const blockUser = async () => {
58
+ await call?.blockUser(participant.userId);
59
+ };
60
+
61
+ const userHasMuteUsersCapability = useHasPermissions(
62
+ OwnCapability.MUTE_USERS,
63
+ );
64
+ const userHasUpdateCallPermissionsCapability = useHasPermissions(
65
+ OwnCapability.UPDATE_CALL_PERMISSIONS,
66
+ );
67
+ const userHasBlockUserCapability = useHasPermissions(
68
+ OwnCapability.BLOCK_USERS,
69
+ );
70
+ const participantCanPublishVideo = participant.publishedTracks.includes(
71
+ SfuModels.TrackType.VIDEO,
72
+ );
73
+ const participantCanPublishAudio = participant.publishedTracks.includes(
74
+ SfuModels.TrackType.AUDIO,
75
+ );
76
+
77
+ const callMediaStreamMutePermissions: (CallParticipantOptionType | null)[] =
78
+ userHasMuteUsersCapability
79
+ ? [
80
+ participantCanPublishVideo
81
+ ? {
82
+ title: 'Mute Video',
83
+ onPressHandler: muteUserVideo,
84
+ }
85
+ : null,
86
+ participantCanPublishAudio
87
+ ? {
88
+ title: 'Mute Audio',
89
+ onPressHandler: muteUserAudio,
90
+ }
91
+ : null,
92
+ ]
93
+ : [];
94
+
95
+ const callMediaStreamPermissions: (CallParticipantOptionType | null)[] =
96
+ userHasUpdateCallPermissionsCapability
97
+ ? [
98
+ {
99
+ icon: <VideoDisabled color={theme.light.text_high_emphasis} />,
100
+ title: 'Disable Video',
101
+ onPressHandler: async () =>
102
+ await revokePermission(OwnCapability.SEND_VIDEO),
103
+ },
104
+ {
105
+ title: 'Disable Audio',
106
+ onPressHandler: async () =>
107
+ await revokePermission(OwnCapability.SEND_AUDIO),
108
+ },
109
+ {
110
+ title: 'Allow Audio',
111
+ onPressHandler: async () =>
112
+ await grantPermission(OwnCapability.SEND_AUDIO),
113
+ },
114
+ {
115
+ title: 'Allow Video',
116
+ onPressHandler: async () =>
117
+ await grantPermission(OwnCapability.SEND_VIDEO),
118
+ },
119
+ {
120
+ title: 'Allow Screen Sharing',
121
+ onPressHandler: async () =>
122
+ await grantPermission(OwnCapability.SCREENSHARE),
123
+ },
124
+ {
125
+ title: 'Disable Screen Sharing',
126
+ onPressHandler: async () =>
127
+ await revokePermission(OwnCapability.SCREENSHARE),
128
+ },
129
+ ]
130
+ : [];
131
+
132
+ const options: (CallParticipantOptionType | null)[] = [
133
+ userHasBlockUserCapability
134
+ ? {
135
+ title: 'Block',
136
+ onPressHandler: blockUser,
137
+ }
138
+ : null,
139
+ ...callMediaStreamMutePermissions,
140
+ ...callMediaStreamPermissions,
141
+ ];
142
+
143
+ const onCloseParticipantOptions = useCallback(() => {
144
+ setSelectedParticipant(undefined);
145
+ }, [setSelectedParticipant]);
146
+
147
+ const showYouLabel = participant.isLoggedInUser;
148
+
149
+ return (
150
+ <View style={styles.container}>
151
+ <View style={styles.menu}>
152
+ <View style={styles.participantInfo}>
153
+ <View style={styles.userInfo}>
154
+ <Avatar radius={theme.avatar.xs} participant={participant} />
155
+
156
+ <Text style={styles.name}>
157
+ {generateParticipantTitle(participant.userId) +
158
+ (showYouLabel ? ' (You)' : '')}
159
+ </Text>
160
+ </View>
161
+
162
+ <Pressable
163
+ style={[styles.svgContainerStyle, theme.icon.sm]}
164
+ onPress={onCloseParticipantOptions}
165
+ >
166
+ <Cross color={theme.light.primary} />
167
+ </Pressable>
168
+ </View>
169
+ <View style={styles.options}>
170
+ {options.map((option, index) => {
171
+ if (!option) {
172
+ return null;
173
+ }
174
+ const applyBottomPadding =
175
+ index < options.length - 1 ? styles.borderBottom : null;
176
+
177
+ const onPressHandler = () => {
178
+ option?.onPressHandler();
179
+ onCloseParticipantOptions();
180
+ };
181
+
182
+ return (
183
+ <Pressable
184
+ style={[applyBottomPadding, styles.option]}
185
+ key={option.title}
186
+ onPress={onPressHandler}
187
+ >
188
+ <View style={[styles.svgContainerStyle, theme.icon.sm]}>
189
+ {option.icon}
190
+ </View>
191
+ <Text style={styles.title}>{option.title}</Text>
192
+ </Pressable>
193
+ );
194
+ })}
195
+ </View>
196
+ </View>
197
+ </View>
198
+ );
199
+ };
200
+
201
+ const styles = StyleSheet.create({
202
+ container: {
203
+ width: '100%',
204
+ height: '100%',
205
+ display: 'flex',
206
+ justifyContent: 'center',
207
+ paddingHorizontal: theme.padding.xl,
208
+ },
209
+ menu: {
210
+ backgroundColor: theme.light.bars,
211
+ borderRadius: theme.rounded.md,
212
+ },
213
+ participantInfo: {
214
+ display: 'flex',
215
+ flexDirection: 'row',
216
+ alignItems: 'center',
217
+ justifyContent: 'space-between',
218
+ padding: theme.padding.md,
219
+ },
220
+ userInfo: {
221
+ display: 'flex',
222
+ flexDirection: 'row',
223
+ alignItems: 'center',
224
+ },
225
+ name: {
226
+ marginLeft: theme.margin.sm,
227
+ ...theme.fonts.subtitleBold,
228
+ color: theme.light.text_high_emphasis,
229
+ },
230
+ svgContainerStyle: {},
231
+ options: {},
232
+ option: {
233
+ paddingHorizontal: theme.padding.md,
234
+ paddingVertical: theme.padding.sm,
235
+ display: 'flex',
236
+ flexDirection: 'row',
237
+ alignItems: 'center',
238
+ },
239
+ title: {
240
+ marginLeft: theme.margin.md,
241
+ color: theme.light.text_high_emphasis,
242
+ ...theme.fonts.subtitle,
243
+ },
244
+ borderBottom: {
245
+ borderBottomColor: theme.light.borders,
246
+ borderBottomWidth: 1,
247
+ },
248
+ });