@ethora/chat-component-rn 26.5.21 → 26.6.10

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 (352) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +3 -3
  3. package/index.js +1 -1
  4. package/lib/commonjs/components/MainComponents/ChatHeader.js +20 -8
  5. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  6. package/lib/commonjs/components/MainComponents/ChatWrapper.js +17 -0
  7. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  8. package/lib/commonjs/components/MainComponents/LanguageSelectorButton.js +62 -0
  9. package/lib/commonjs/components/MainComponents/LanguageSelectorButton.js.map +1 -0
  10. package/lib/commonjs/components/MainComponents/LanguageSelectorModal.js +253 -0
  11. package/lib/commonjs/components/MainComponents/LanguageSelectorModal.js.map +1 -0
  12. package/lib/commonjs/components/MainComponents/RoomList.js +23 -9
  13. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  14. package/lib/commonjs/components/MessageBubble/Message.js +72 -11
  15. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  16. package/lib/commonjs/components/MessageBubble/MessageTranslate.js +160 -0
  17. package/lib/commonjs/components/MessageBubble/MessageTranslate.js.map +1 -0
  18. package/lib/commonjs/components/MessageBubble/TranslatedMessageBody.js +59 -0
  19. package/lib/commonjs/components/MessageBubble/TranslatedMessageBody.js.map +1 -0
  20. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +3 -3
  21. package/lib/commonjs/components/UsersList/UsersList.js +3 -1
  22. package/lib/commonjs/components/UsersList/UsersList.js.map +1 -1
  23. package/lib/commonjs/components/VideoCalls/CallButtons.js +159 -0
  24. package/lib/commonjs/components/VideoCalls/CallButtons.js.map +1 -0
  25. package/lib/commonjs/components/VideoCalls/CallIcons.js +328 -0
  26. package/lib/commonjs/components/VideoCalls/CallIcons.js.map +1 -0
  27. package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +550 -0
  28. package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -0
  29. package/lib/commonjs/components/VideoCalls/VideoCallSession.js +636 -0
  30. package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -0
  31. package/lib/commonjs/components/VideoCalls/livekitRuntime.js +91 -0
  32. package/lib/commonjs/components/VideoCalls/livekitRuntime.js.map +1 -0
  33. package/lib/commonjs/components/VideoCalls/useCallKeep.js +215 -0
  34. package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -0
  35. package/lib/commonjs/components/styled/AudioMessage.js +91 -33
  36. package/lib/commonjs/components/styled/AudioMessage.js.map +1 -1
  37. package/lib/commonjs/components/styled/RoomListComponents/index.js +13 -1
  38. package/lib/commonjs/components/styled/RoomListComponents/index.js.map +1 -1
  39. package/lib/commonjs/components/styled/SendInput.js +83 -49
  40. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  41. package/lib/commonjs/components/styled/StyledComponents.js +9 -4
  42. package/lib/commonjs/components/styled/StyledComponents.js.map +1 -1
  43. package/lib/commonjs/components/styled/StyledInputComponents/Composing.js +2 -0
  44. package/lib/commonjs/components/styled/StyledInputComponents/Composing.js.map +1 -1
  45. package/lib/commonjs/context/xmppProvider.js +10 -3
  46. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  47. package/lib/commonjs/helpers/callLogMessage.js +162 -0
  48. package/lib/commonjs/helpers/callLogMessage.js.map +1 -0
  49. package/lib/commonjs/helpers/callPush.js +61 -0
  50. package/lib/commonjs/helpers/callPush.js.map +1 -0
  51. package/lib/commonjs/helpers/constants/LANGUAGE_OPTIONS.js +24 -12
  52. package/lib/commonjs/helpers/constants/LANGUAGE_OPTIONS.js.map +1 -1
  53. package/lib/commonjs/helpers/createUserNameFromSetUser.js +51 -2
  54. package/lib/commonjs/helpers/createUserNameFromSetUser.js.map +1 -1
  55. package/lib/commonjs/helpers/getDataFromXml.js +19 -1
  56. package/lib/commonjs/helpers/getDataFromXml.js.map +1 -1
  57. package/lib/commonjs/helpers/incomingCallFromPush.js +74 -0
  58. package/lib/commonjs/helpers/incomingCallFromPush.js.map +1 -0
  59. package/lib/commonjs/helpers/insertMessageWithDelimiter.js +13 -1
  60. package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
  61. package/lib/commonjs/helpers/isUnresolvedSenderId.js +32 -0
  62. package/lib/commonjs/helpers/isUnresolvedSenderId.js.map +1 -0
  63. package/lib/commonjs/hooks/useChatSettingState.js +2 -0
  64. package/lib/commonjs/hooks/useChatSettingState.js.map +1 -1
  65. package/lib/commonjs/hooks/useMessageTranslation.js +51 -0
  66. package/lib/commonjs/hooks/useMessageTranslation.js.map +1 -0
  67. package/lib/commonjs/hooks/usePushNotifications.js +13 -3
  68. package/lib/commonjs/hooks/usePushNotifications.js.map +1 -1
  69. package/lib/commonjs/hooks/useSendMessage.js +12 -2
  70. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  71. package/lib/commonjs/i18n/strings.js +1308 -0
  72. package/lib/commonjs/i18n/strings.js.map +1 -0
  73. package/lib/commonjs/i18n/useT.js +60 -0
  74. package/lib/commonjs/i18n/useT.js.map +1 -0
  75. package/lib/commonjs/main.js +44 -0
  76. package/lib/commonjs/main.js.map +1 -1
  77. package/lib/commonjs/networking/api-requests/push.api.js +34 -5
  78. package/lib/commonjs/networking/api-requests/push.api.js.map +1 -1
  79. package/lib/commonjs/networking/api-requests/rooms.api.js +108 -0
  80. package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
  81. package/lib/commonjs/networking/callTokenStanza.js +531 -0
  82. package/lib/commonjs/networking/callTokenStanza.js.map +1 -0
  83. package/lib/commonjs/networking/outboundQueue.js.map +1 -1
  84. package/lib/commonjs/networking/stanzaHandlers.js +75 -8
  85. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  86. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +47 -11
  87. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  88. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +2 -1
  89. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  90. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +42 -5
  91. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  92. package/lib/commonjs/networking/xmppClient.js +78 -16
  93. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  94. package/lib/commonjs/roomStore/callSlice.js +144 -0
  95. package/lib/commonjs/roomStore/callSlice.js.map +1 -0
  96. package/lib/commonjs/roomStore/chatSettingsSlice.js +8 -2
  97. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  98. package/lib/commonjs/roomStore/index.js +7 -1
  99. package/lib/commonjs/roomStore/index.js.map +1 -1
  100. package/lib/commonjs/roomStore/persistence.js +69 -5
  101. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  102. package/lib/commonjs/roomStore/roomsSlice.js +136 -6
  103. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  104. package/lib/commonjs/utils/translateModePolicy.js +33 -0
  105. package/lib/commonjs/utils/translateModePolicy.js.map +1 -0
  106. package/lib/module/components/MainComponents/ChatHeader.js +20 -8
  107. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  108. package/lib/module/components/MainComponents/ChatWrapper.js +17 -0
  109. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  110. package/lib/module/components/MainComponents/LanguageSelectorButton.js +57 -0
  111. package/lib/module/components/MainComponents/LanguageSelectorButton.js.map +1 -0
  112. package/lib/module/components/MainComponents/LanguageSelectorModal.js +246 -0
  113. package/lib/module/components/MainComponents/LanguageSelectorModal.js.map +1 -0
  114. package/lib/module/components/MainComponents/RoomList.js +23 -9
  115. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  116. package/lib/module/components/MessageBubble/Message.js +73 -12
  117. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  118. package/lib/module/components/MessageBubble/MessageTranslate.js +155 -0
  119. package/lib/module/components/MessageBubble/MessageTranslate.js.map +1 -0
  120. package/lib/module/components/MessageBubble/TranslatedMessageBody.js +53 -0
  121. package/lib/module/components/MessageBubble/TranslatedMessageBody.js.map +1 -0
  122. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +3 -3
  123. package/lib/module/components/UsersList/UsersList.js +3 -1
  124. package/lib/module/components/UsersList/UsersList.js.map +1 -1
  125. package/lib/module/components/VideoCalls/CallButtons.js +154 -0
  126. package/lib/module/components/VideoCalls/CallButtons.js.map +1 -0
  127. package/lib/module/components/VideoCalls/CallIcons.js +309 -0
  128. package/lib/module/components/VideoCalls/CallIcons.js.map +1 -0
  129. package/lib/module/components/VideoCalls/VideoCallOverlay.js +544 -0
  130. package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -0
  131. package/lib/module/components/VideoCalls/VideoCallSession.js +630 -0
  132. package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -0
  133. package/lib/module/components/VideoCalls/livekitRuntime.js +83 -0
  134. package/lib/module/components/VideoCalls/livekitRuntime.js.map +1 -0
  135. package/lib/module/components/VideoCalls/useCallKeep.js +210 -0
  136. package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -0
  137. package/lib/module/components/styled/AudioMessage.js +91 -33
  138. package/lib/module/components/styled/AudioMessage.js.map +1 -1
  139. package/lib/module/components/styled/RoomListComponents/index.js +13 -1
  140. package/lib/module/components/styled/RoomListComponents/index.js.map +1 -1
  141. package/lib/module/components/styled/SendInput.js +83 -49
  142. package/lib/module/components/styled/SendInput.js.map +1 -1
  143. package/lib/module/components/styled/StyledComponents.js +9 -4
  144. package/lib/module/components/styled/StyledComponents.js.map +1 -1
  145. package/lib/module/components/styled/StyledInputComponents/Composing.js +2 -0
  146. package/lib/module/components/styled/StyledInputComponents/Composing.js.map +1 -1
  147. package/lib/module/context/xmppProvider.js +11 -4
  148. package/lib/module/context/xmppProvider.js.map +1 -1
  149. package/lib/module/helpers/callLogMessage.js +152 -0
  150. package/lib/module/helpers/callLogMessage.js.map +1 -0
  151. package/lib/module/helpers/callPush.js +55 -0
  152. package/lib/module/helpers/callPush.js.map +1 -0
  153. package/lib/module/helpers/constants/LANGUAGE_OPTIONS.js +24 -12
  154. package/lib/module/helpers/constants/LANGUAGE_OPTIONS.js.map +1 -1
  155. package/lib/module/helpers/createUserNameFromSetUser.js +49 -1
  156. package/lib/module/helpers/createUserNameFromSetUser.js.map +1 -1
  157. package/lib/module/helpers/getDataFromXml.js +20 -1
  158. package/lib/module/helpers/getDataFromXml.js.map +1 -1
  159. package/lib/module/helpers/incomingCallFromPush.js +70 -0
  160. package/lib/module/helpers/incomingCallFromPush.js.map +1 -0
  161. package/lib/module/helpers/insertMessageWithDelimiter.js +13 -1
  162. package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
  163. package/lib/module/helpers/isUnresolvedSenderId.js +27 -0
  164. package/lib/module/helpers/isUnresolvedSenderId.js.map +1 -0
  165. package/lib/module/hooks/useChatSettingState.js +2 -0
  166. package/lib/module/hooks/useChatSettingState.js.map +1 -1
  167. package/lib/module/hooks/useMessageTranslation.js +46 -0
  168. package/lib/module/hooks/useMessageTranslation.js.map +1 -0
  169. package/lib/module/hooks/usePushNotifications.js +13 -3
  170. package/lib/module/hooks/usePushNotifications.js.map +1 -1
  171. package/lib/module/hooks/useSendMessage.js +12 -2
  172. package/lib/module/hooks/useSendMessage.js.map +1 -1
  173. package/lib/module/i18n/strings.js +1300 -0
  174. package/lib/module/i18n/strings.js.map +1 -0
  175. package/lib/module/i18n/useT.js +55 -0
  176. package/lib/module/i18n/useT.js.map +1 -0
  177. package/lib/module/main.js +7 -1
  178. package/lib/module/main.js.map +1 -1
  179. package/lib/module/networking/api-requests/push.api.js +35 -5
  180. package/lib/module/networking/api-requests/push.api.js.map +1 -1
  181. package/lib/module/networking/api-requests/rooms.api.js +109 -1
  182. package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
  183. package/lib/module/networking/callTokenStanza.js +526 -0
  184. package/lib/module/networking/callTokenStanza.js.map +1 -0
  185. package/lib/module/networking/outboundQueue.js.map +1 -1
  186. package/lib/module/networking/stanzaHandlers.js +76 -9
  187. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  188. package/lib/module/networking/xmpp/handleStanzas.xmpp.js +48 -11
  189. package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  190. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +1 -1
  191. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  192. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +43 -5
  193. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  194. package/lib/module/networking/xmppClient.js +78 -16
  195. package/lib/module/networking/xmppClient.js.map +1 -1
  196. package/lib/module/roomStore/callSlice.js +130 -0
  197. package/lib/module/roomStore/callSlice.js.map +1 -0
  198. package/lib/module/roomStore/chatSettingsSlice.js +6 -1
  199. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  200. package/lib/module/roomStore/index.js +6 -1
  201. package/lib/module/roomStore/index.js.map +1 -1
  202. package/lib/module/roomStore/persistence.js +69 -5
  203. package/lib/module/roomStore/persistence.js.map +1 -1
  204. package/lib/module/roomStore/roomsSlice.js +134 -5
  205. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  206. package/lib/module/utils/translateModePolicy.js +27 -0
  207. package/lib/module/utils/translateModePolicy.js.map +1 -0
  208. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  209. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  210. package/lib/typescript/components/MainComponents/LanguageSelectorButton.d.ts +12 -0
  211. package/lib/typescript/components/MainComponents/LanguageSelectorButton.d.ts.map +1 -0
  212. package/lib/typescript/components/MainComponents/LanguageSelectorModal.d.ts +31 -0
  213. package/lib/typescript/components/MainComponents/LanguageSelectorModal.d.ts.map +1 -0
  214. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  215. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  216. package/lib/typescript/components/MessageBubble/MessageTranslate.d.ts +44 -0
  217. package/lib/typescript/components/MessageBubble/MessageTranslate.d.ts.map +1 -0
  218. package/lib/typescript/components/MessageBubble/TranslatedMessageBody.d.ts +24 -0
  219. package/lib/typescript/components/MessageBubble/TranslatedMessageBody.d.ts.map +1 -0
  220. package/lib/typescript/components/UsersList/UsersList.d.ts.map +1 -1
  221. package/lib/typescript/components/VideoCalls/CallButtons.d.ts +16 -0
  222. package/lib/typescript/components/VideoCalls/CallButtons.d.ts.map +1 -0
  223. package/lib/typescript/components/VideoCalls/CallIcons.d.ts +30 -0
  224. package/lib/typescript/components/VideoCalls/CallIcons.d.ts.map +1 -0
  225. package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts +10 -0
  226. package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -0
  227. package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts +38 -0
  228. package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -0
  229. package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts +52 -0
  230. package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts.map +1 -0
  231. package/lib/typescript/components/VideoCalls/useCallKeep.d.ts +32 -0
  232. package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -0
  233. package/lib/typescript/components/styled/AudioMessage.d.ts.map +1 -1
  234. package/lib/typescript/components/styled/RoomListComponents/index.d.ts.map +1 -1
  235. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  236. package/lib/typescript/components/styled/StyledComponents.d.ts.map +1 -1
  237. package/lib/typescript/components/styled/StyledInputComponents/Composing.d.ts.map +1 -1
  238. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  239. package/lib/typescript/helpers/callLogMessage.d.ts +34 -0
  240. package/lib/typescript/helpers/callLogMessage.d.ts.map +1 -0
  241. package/lib/typescript/helpers/callPush.d.ts +23 -0
  242. package/lib/typescript/helpers/callPush.d.ts.map +1 -0
  243. package/lib/typescript/helpers/constants/LANGUAGE_OPTIONS.d.ts +14 -0
  244. package/lib/typescript/helpers/constants/LANGUAGE_OPTIONS.d.ts.map +1 -1
  245. package/lib/typescript/helpers/createUserNameFromSetUser.d.ts +33 -1
  246. package/lib/typescript/helpers/createUserNameFromSetUser.d.ts.map +1 -1
  247. package/lib/typescript/helpers/getDataFromXml.d.ts.map +1 -1
  248. package/lib/typescript/helpers/incomingCallFromPush.d.ts +14 -0
  249. package/lib/typescript/helpers/incomingCallFromPush.d.ts.map +1 -0
  250. package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
  251. package/lib/typescript/helpers/isUnresolvedSenderId.d.ts +2 -0
  252. package/lib/typescript/helpers/isUnresolvedSenderId.d.ts.map +1 -0
  253. package/lib/typescript/hooks/useChatSettingState.d.ts +2 -1
  254. package/lib/typescript/hooks/useChatSettingState.d.ts.map +1 -1
  255. package/lib/typescript/hooks/useMessageTranslation.d.ts +25 -0
  256. package/lib/typescript/hooks/useMessageTranslation.d.ts.map +1 -0
  257. package/lib/typescript/hooks/usePushNotifications.d.ts.map +1 -1
  258. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  259. package/lib/typescript/i18n/strings.d.ts +16 -0
  260. package/lib/typescript/i18n/strings.d.ts.map +1 -0
  261. package/lib/typescript/i18n/useT.d.ts +34 -0
  262. package/lib/typescript/i18n/useT.d.ts.map +1 -0
  263. package/lib/typescript/main.d.ts +6 -1
  264. package/lib/typescript/main.d.ts.map +1 -1
  265. package/lib/typescript/networking/api-requests/push.api.d.ts.map +1 -1
  266. package/lib/typescript/networking/api-requests/rooms.api.d.ts +3 -0
  267. package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
  268. package/lib/typescript/networking/callTokenStanza.d.ts +14 -0
  269. package/lib/typescript/networking/callTokenStanza.d.ts.map +1 -0
  270. package/lib/typescript/networking/outboundQueue.d.ts +1 -0
  271. package/lib/typescript/networking/outboundQueue.d.ts.map +1 -1
  272. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  273. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
  274. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts +10 -0
  275. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
  276. package/lib/typescript/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts +16 -1
  277. package/lib/typescript/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts.map +1 -1
  278. package/lib/typescript/networking/xmppClient.d.ts +1 -1
  279. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  280. package/lib/typescript/roomStore/callSlice.d.ts +39 -0
  281. package/lib/typescript/roomStore/callSlice.d.ts.map +1 -0
  282. package/lib/typescript/roomStore/chatSettingsSlice.d.ts +10 -1
  283. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  284. package/lib/typescript/roomStore/index.d.ts +4 -0
  285. package/lib/typescript/roomStore/index.d.ts.map +1 -1
  286. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  287. package/lib/typescript/roomStore/roomsSlice.d.ts +6 -1
  288. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  289. package/lib/typescript/types/models/language.model.d.ts +1 -1
  290. package/lib/typescript/types/models/language.model.d.ts.map +1 -1
  291. package/lib/typescript/types/models/message.model.d.ts +7 -0
  292. package/lib/typescript/types/models/message.model.d.ts.map +1 -1
  293. package/lib/typescript/types/types.d.ts +135 -1
  294. package/lib/typescript/types/types.d.ts.map +1 -1
  295. package/lib/typescript/utils/translateModePolicy.d.ts +20 -0
  296. package/lib/typescript/utils/translateModePolicy.d.ts.map +1 -0
  297. package/package.json +20 -4
  298. package/src/components/MainComponents/ChatHeader.tsx +30 -9
  299. package/src/components/MainComponents/ChatWrapper.tsx +15 -0
  300. package/src/components/MainComponents/LanguageSelectorButton.tsx +53 -0
  301. package/src/components/MainComponents/LanguageSelectorModal.tsx +276 -0
  302. package/src/components/MainComponents/RoomList.tsx +27 -9
  303. package/src/components/MessageBubble/Message.tsx +109 -17
  304. package/src/components/MessageBubble/MessageTranslate.tsx +182 -0
  305. package/src/components/MessageBubble/TranslatedMessageBody.tsx +59 -0
  306. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +3 -3
  307. package/src/components/UsersList/UsersList.tsx +3 -1
  308. package/src/components/VideoCalls/CallButtons.tsx +171 -0
  309. package/src/components/VideoCalls/CallIcons.tsx +246 -0
  310. package/src/components/VideoCalls/VideoCallOverlay.tsx +627 -0
  311. package/src/components/VideoCalls/VideoCallSession.tsx +675 -0
  312. package/src/components/VideoCalls/livekitRuntime.ts +100 -0
  313. package/src/components/VideoCalls/useCallKeep.ts +252 -0
  314. package/src/components/styled/AudioMessage.tsx +97 -37
  315. package/src/components/styled/RoomListComponents/index.tsx +15 -3
  316. package/src/components/styled/SendInput.tsx +94 -51
  317. package/src/components/styled/StyledComponents.ts +9 -6
  318. package/src/components/styled/StyledInputComponents/Composing.tsx +7 -1
  319. package/src/context/xmppProvider.tsx +14 -1
  320. package/src/helpers/callLogMessage.ts +174 -0
  321. package/src/helpers/callPush.ts +90 -0
  322. package/src/helpers/constants/LANGUAGE_OPTIONS.ts +20 -5
  323. package/src/helpers/createUserNameFromSetUser.ts +68 -2
  324. package/src/helpers/getDataFromXml.ts +40 -4
  325. package/src/helpers/incomingCallFromPush.ts +83 -0
  326. package/src/helpers/insertMessageWithDelimiter.ts +27 -9
  327. package/src/helpers/isUnresolvedSenderId.ts +25 -0
  328. package/src/hooks/useChatSettingState.tsx +4 -0
  329. package/src/hooks/useMessageTranslation.ts +70 -0
  330. package/src/hooks/usePushNotifications.ts +15 -3
  331. package/src/hooks/useSendMessage.tsx +15 -2
  332. package/src/i18n/strings.ts +1322 -0
  333. package/src/i18n/useT.ts +56 -0
  334. package/src/main.ts +18 -1
  335. package/src/networking/api-requests/push.api.ts +39 -8
  336. package/src/networking/api-requests/rooms.api.ts +109 -1
  337. package/src/networking/callTokenStanza.ts +636 -0
  338. package/src/networking/outboundQueue.ts +1 -0
  339. package/src/networking/stanzaHandlers.ts +74 -15
  340. package/src/networking/xmpp/handleStanzas.xmpp.ts +52 -11
  341. package/src/networking/xmpp/sendTextMessage.xmpp.ts +1 -1
  342. package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +41 -5
  343. package/src/networking/xmppClient.ts +97 -28
  344. package/src/roomStore/callSlice.ts +190 -0
  345. package/src/roomStore/chatSettingsSlice.ts +16 -0
  346. package/src/roomStore/index.ts +5 -0
  347. package/src/roomStore/persistence.ts +103 -3
  348. package/src/roomStore/roomsSlice.ts +149 -4
  349. package/src/types/models/language.model.ts +17 -1
  350. package/src/types/models/message.model.ts +9 -0
  351. package/src/types/types.ts +142 -2
  352. package/src/utils/translateModePolicy.ts +31 -0
@@ -0,0 +1,675 @@
1
+ import React, {
2
+ FC,
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
9
+ import {
10
+ ActivityIndicator,
11
+ Platform,
12
+ StyleSheet,
13
+ Text,
14
+ TouchableOpacity,
15
+ View,
16
+ } from 'react-native';
17
+ import { VideoCallIcons } from '../../types/types';
18
+ import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlaceholder';
19
+ import {
20
+ CameraOffIcon,
21
+ CameraOnIcon,
22
+ HangUpIcon,
23
+ MicOffIcon,
24
+ MicOnIcon,
25
+ MinimizeIcon,
26
+ ExpandIcon,
27
+ SpeakerIcon,
28
+ SpeakerOffIcon,
29
+ SwitchCameraIcon,
30
+ } from './CallIcons';
31
+ import { ensureLiveKitGlobals, loadLiveKit } from './livekitRuntime';
32
+ import { useT } from '../../i18n/useT';
33
+
34
+ const DEFAULT_PRIMARY = '#0052CD';
35
+ const SURFACE_DARK = '#0B1220';
36
+ const TEXT_ON_DARK = '#FFFFFF';
37
+ const TEXT_MUTED = '#9AA4B2';
38
+ const DANGER = '#E53935';
39
+ const CONTROL_IDLE = 'rgba(255, 255, 255, 0.16)';
40
+
41
+ // A device (camera/mic) permission/availability problem during enable —
42
+ // distinct from a connect failure. Mirrors the web SDK's humanizeDeviceError:
43
+ // this must never fail the whole call (the user can still talk/hear once
44
+ // they re-allow or plug in a device), only surface a dismissible hint. Most
45
+ // relevant on the iOS Simulator, which has no real camera at all — every
46
+ // video call would otherwise die at setCameraEnabled before ever connecting.
47
+ const humanizeDeviceError = (error: unknown, t: (key: string) => string): string => {
48
+ const name = (error as { name?: string })?.name || '';
49
+ const text = String((error as Error)?.message || '').toLowerCase();
50
+ if (name === 'NotAllowedError' || text.includes('permission') || text.includes('denied')) {
51
+ return t('call.error.deviceBlocked');
52
+ }
53
+ if (name === 'NotFoundError' || text.includes('not found')) {
54
+ return t('call.error.deviceNotFound');
55
+ }
56
+ if (name === 'NotReadableError' || text.includes('in use')) {
57
+ return t('call.error.deviceInUse');
58
+ }
59
+ return t('call.error.deviceGeneric');
60
+ };
61
+
62
+ export interface VideoCallSessionProps {
63
+ token: string;
64
+ livekitUrl: string;
65
+ kind?: 'audio' | 'video';
66
+ /** Display name of the peer, shown on the audio-call canvas. */
67
+ peerName?: string | null;
68
+ primaryColor?: string;
69
+ icons?: VideoCallIcons;
70
+ /** Start with the camera on (video calls). Default true. */
71
+ startWithCameraOn?: boolean;
72
+ /** Start with the microphone on. Default true. */
73
+ startWithMicOn?: boolean;
74
+ /** Render the compact floating-panel layout instead of the full screen. */
75
+ minimized?: boolean;
76
+ /** Toggle between full and minimized layouts (keeps the session mounted). */
77
+ onToggleMinimize?: () => void;
78
+ onConnected?: () => void;
79
+ onError?: (message: string) => void;
80
+ onHangup: () => void;
81
+ }
82
+
83
+ /**
84
+ * The LiveKit session, React Native edition.
85
+ *
86
+ * The web SDK builds this on `@livekit/components-react` hooks. That package
87
+ * assumes a DOM, so here we drive `livekit-client`'s imperative `Room` API
88
+ * directly and keep the derived bits (subscribed tracks, mute flags,
89
+ * connection state) in local React state, re-syncing on RoomEvent. Only the
90
+ * actual video surface comes from `@livekit/react-native` (`<VideoTrack />`),
91
+ * which wraps the native renderer.
92
+ *
93
+ * Requires `@livekit/react-native` >= 2.7 for the `trackRef` prop shape used
94
+ * below. Both LiveKit packages are optional peer deps, see livekitRuntime.ts.
95
+ */
96
+ export const VideoCallSession: FC<VideoCallSessionProps> = ({
97
+ token,
98
+ livekitUrl,
99
+ kind = 'video',
100
+ peerName,
101
+ primaryColor = DEFAULT_PRIMARY,
102
+ icons,
103
+ startWithCameraOn = true,
104
+ startWithMicOn = true,
105
+ minimized = false,
106
+ onToggleMinimize,
107
+ onConnected,
108
+ onError,
109
+ onHangup,
110
+ }) => {
111
+ const t = useT();
112
+ const runtime = useMemo(() => loadLiveKit(), []);
113
+
114
+ const roomRef = useRef<any>(null);
115
+ const [connected, setConnected] = useState(false);
116
+ const [deviceError, setDeviceError] = useState<string | null>(null);
117
+ const [micOn, setMicOn] = useState(startWithMicOn);
118
+ const [cameraOn, setCameraOn] = useState(kind === 'video' && startWithCameraOn);
119
+ const [speakerOn, setSpeakerOn] = useState(kind === 'video');
120
+ const [remoteVideo, setRemoteVideo] = useState<any>(null);
121
+ const [localVideo, setLocalVideo] = useState<any>(null);
122
+ const [remoteName, setRemoteName] = useState<string | null>(null);
123
+ const [remoteJoined, setRemoteJoined] = useState(false);
124
+
125
+ // Callbacks are read through a ref inside the connect effect so that a
126
+ // parent re-render (new inline arrow props) never re-runs connect and
127
+ // tears down a live call. Same guard the web session uses.
128
+ const callbacksRef = useRef({ onConnected, onError, onHangup });
129
+ useEffect(() => {
130
+ callbacksRef.current = { onConnected, onError, onHangup };
131
+ }, [onConnected, onError, onHangup]);
132
+
133
+ // ---- track plumbing -------------------------------------------------
134
+
135
+ // Build the { participant, publication, source } shape `<VideoTrack />`
136
+ // expects, from whatever camera publication a participant currently has.
137
+ const toTrackRef = useCallback(
138
+ (participant: any, Track: any) => {
139
+ if (!participant) {return null;}
140
+ const publication = participant.getTrackPublication?.(Track.Source.Camera);
141
+ if (!publication?.track) {return null;}
142
+ return { participant, publication, source: Track.Source.Camera };
143
+ },
144
+ []
145
+ );
146
+
147
+ const resyncTracks = useCallback(() => {
148
+ const room = roomRef.current;
149
+ if (!room || !runtime) {return;}
150
+ const { Track } = runtime;
151
+
152
+ const remote = Array.from(room.remoteParticipants?.values?.() || [])[0] as any;
153
+ setRemoteJoined(!!remote);
154
+ setRemoteName(remote?.name || remote?.identity || null);
155
+ setRemoteVideo(toTrackRef(remote, Track));
156
+ setLocalVideo(toTrackRef(room.localParticipant, Track));
157
+ }, [runtime, toTrackRef]);
158
+
159
+ // ---- connect / teardown ---------------------------------------------
160
+
161
+ useEffect(() => {
162
+ if (!runtime) {
163
+ callbacksRef.current.onError?.(t('call.error.sdkMissing'));
164
+ return;
165
+ }
166
+ if (!token || !livekitUrl) {
167
+ callbacksRef.current.onError?.(t('call.error.missingConfig'));
168
+ return;
169
+ }
170
+
171
+ ensureLiveKitGlobals(runtime);
172
+
173
+ const { Room, RoomEvent, AudioSession } = runtime;
174
+ let disposed = false;
175
+
176
+ // adaptiveStream + dynacast keep mobile data and battery sane: the SDK
177
+ // drops resolution for a small tile and stops publishing layers nobody
178
+ // subscribes to. Both are safe defaults for 1:1.
179
+ const room = new Room({
180
+ adaptiveStream: true,
181
+ dynacast: true,
182
+ });
183
+ roomRef.current = room;
184
+
185
+ const onStateChange = () => {
186
+ if (disposed) {return;}
187
+ resyncTracks();
188
+ };
189
+
190
+ room
191
+ .on(RoomEvent.TrackSubscribed, onStateChange)
192
+ .on(RoomEvent.TrackUnsubscribed, onStateChange)
193
+ .on(RoomEvent.LocalTrackPublished, onStateChange)
194
+ .on(RoomEvent.LocalTrackUnpublished, onStateChange)
195
+ .on(RoomEvent.TrackMuted, onStateChange)
196
+ .on(RoomEvent.TrackUnmuted, onStateChange)
197
+ .on(RoomEvent.ParticipantConnected, onStateChange)
198
+ .on(RoomEvent.MediaDevicesError, (error: unknown) => {
199
+ if (disposed) {return;}
200
+ setDeviceError(humanizeDeviceError(error, t));
201
+ })
202
+ .on(RoomEvent.ParticipantDisconnected, () => {
203
+ if (disposed) {return;}
204
+ resyncTracks();
205
+ // 1:1 semantics: the other side leaving IS the end of the call.
206
+ // Without this the local user sits on a black canvas until they
207
+ // hang up manually.
208
+ callbacksRef.current.onHangup?.();
209
+ })
210
+ .on(RoomEvent.Disconnected, () => {
211
+ if (disposed) {return;}
212
+ setConnected(false);
213
+ callbacksRef.current.onHangup?.();
214
+ });
215
+
216
+ (async () => {
217
+ // Joining the LiveKit room is the only step whose failure should fail
218
+ // the call (bad token, network, server down). Everything after this
219
+ // is best-effort device setup — see the separate try/catches below.
220
+ try {
221
+ // Claims audio focus and sets the right native audio mode. Must
222
+ // start BEFORE connecting or the first seconds route to the
223
+ // earpiece on Android even for a video call.
224
+ await AudioSession.startAudioSession();
225
+ await room.connect(livekitUrl, token);
226
+ } catch (error: any) {
227
+ if (disposed) {return;}
228
+ callbacksRef.current.onError?.(
229
+ String(error?.message || '') || t('call.error.connectFailed')
230
+ );
231
+ return;
232
+ }
233
+ if (disposed) {return;}
234
+
235
+ // Devices are best-effort: if the mic/camera prompt is blocked, or —
236
+ // notably — this is the iOS Simulator (no real camera hardware at
237
+ // all), the call still connects. The user can hear/be heard on
238
+ // audio and re-enable the camera later; failing the whole call here
239
+ // (as an earlier version did, sharing one try/catch with `connect`)
240
+ // meant every video call died before ever reaching the ring-answered
241
+ // screen — indistinguishable from "Accept doesn't work".
242
+ try {
243
+ await room.localParticipant.setMicrophoneEnabled(startWithMicOn);
244
+ } catch (error) {
245
+ if (!disposed) {setDeviceError(humanizeDeviceError(error, t));}
246
+ }
247
+ if (kind === 'video') {
248
+ try {
249
+ await room.localParticipant.setCameraEnabled(startWithCameraOn);
250
+ } catch (error) {
251
+ if (!disposed) {setDeviceError(humanizeDeviceError(error, t));}
252
+ }
253
+ }
254
+ if (disposed) {return;}
255
+
256
+ // Video calls belong on the loudspeaker, audio calls on the
257
+ // earpiece, matching what every native dialer does.
258
+ try {
259
+ await AudioSession.setAppleAudioConfiguration?.({
260
+ audioCategory: 'playAndRecord',
261
+ audioMode: kind === 'video' ? 'videoChat' : 'voiceChat',
262
+ });
263
+ } catch {
264
+ // iOS-only helper, absent on Android builds of the SDK.
265
+ }
266
+
267
+ setConnected(true);
268
+ resyncTracks();
269
+ callbacksRef.current.onConnected?.();
270
+ })();
271
+
272
+ return () => {
273
+ disposed = true;
274
+ try {
275
+ room.removeAllListeners?.();
276
+ void room.disconnect();
277
+ } catch {
278
+ // Already gone.
279
+ }
280
+ try {
281
+ void AudioSession.stopAudioSession();
282
+ } catch {
283
+ // Never let teardown throw, it runs during unmount.
284
+ }
285
+ roomRef.current = null;
286
+ };
287
+ // Connect exactly once per (url, token, kind). Everything else is read
288
+ // through refs, see callbacksRef above.
289
+ // eslint-disable-next-line react-hooks/exhaustive-deps
290
+ }, [runtime, livekitUrl, token, kind]);
291
+
292
+ // ---- controls --------------------------------------------------------
293
+
294
+ const toggleMic = useCallback(async () => {
295
+ const room = roomRef.current;
296
+ if (!room) {return;}
297
+ const next = !micOn;
298
+ setMicOn(next);
299
+ try {
300
+ await room.localParticipant.setMicrophoneEnabled(next);
301
+ } catch {
302
+ setMicOn(!next);
303
+ }
304
+ }, [micOn]);
305
+
306
+ const toggleCamera = useCallback(async () => {
307
+ const room = roomRef.current;
308
+ if (!room) {return;}
309
+ const next = !cameraOn;
310
+ setCameraOn(next);
311
+ try {
312
+ await room.localParticipant.setCameraEnabled(next);
313
+ resyncTracks();
314
+ } catch {
315
+ setCameraOn(!next);
316
+ }
317
+ }, [cameraOn, resyncTracks]);
318
+
319
+ const switchCamera = useCallback(async () => {
320
+ const room = roomRef.current;
321
+ if (!room || !runtime) {return;}
322
+ try {
323
+ const publication = room.localParticipant.getTrackPublication?.(
324
+ runtime.Track.Source.Camera
325
+ );
326
+ const track = publication?.track;
327
+ // `restartTrack` with the flipped facingMode is the portable way to
328
+ // switch cameras; the RN SDK's `_switchCamera` helper is internal and
329
+ // has moved between versions.
330
+ await track?.restartTrack?.({
331
+ facingMode: track?.mediaStreamTrack?.getSettings?.().facingMode ===
332
+ 'environment'
333
+ ? 'user'
334
+ : 'environment',
335
+ });
336
+ resyncTracks();
337
+ } catch {
338
+ // Device has a single camera, or the track is mid-restart.
339
+ }
340
+ }, [runtime, resyncTracks]);
341
+
342
+ const toggleSpeaker = useCallback(async () => {
343
+ const next = !speakerOn;
344
+ setSpeakerOn(next);
345
+ if (!runtime) {return;}
346
+ try {
347
+ if (Platform.OS === 'android') {
348
+ await runtime.AudioSession.setAndroidAudioConfiguration?.({
349
+ preferredOutputList: next ? ['speaker'] : ['earpiece'],
350
+ });
351
+ } else {
352
+ await runtime.AudioSession.setAppleAudioConfiguration?.({
353
+ audioCategory: 'playAndRecord',
354
+ audioCategoryOptions: next ? ['defaultToSpeaker'] : [],
355
+ audioMode: next ? 'videoChat' : 'voiceChat',
356
+ });
357
+ }
358
+ } catch {
359
+ setSpeakerOn(!next);
360
+ }
361
+ }, [runtime, speakerOn]);
362
+
363
+ // ---- render ----------------------------------------------------------
364
+
365
+ if (!runtime) {
366
+ return (
367
+ <View style={[styles.canvas, styles.centered]}>
368
+ <Text style={styles.errorText}>{t('call.error.sdkMissing')}</Text>
369
+ </View>
370
+ );
371
+ }
372
+
373
+ const { VideoTrack } = runtime;
374
+ const displayName = remoteName || peerName || t('call.unknownCaller');
375
+ const isVideo = kind === 'video';
376
+ const showRemoteVideo = isVideo && !!remoteVideo;
377
+
378
+ if (minimized) {
379
+ return (
380
+ <View style={styles.miniBar}>
381
+ <View style={styles.miniInfo}>
382
+ <Text numberOfLines={1} style={styles.miniName}>
383
+ {displayName}
384
+ </Text>
385
+ <Text style={styles.miniStatus}>
386
+ {connected
387
+ ? remoteJoined
388
+ ? t('call.status.inCall')
389
+ : t('call.status.waitingForPeer')
390
+ : t('call.status.connecting')}
391
+ </Text>
392
+ </View>
393
+ <TouchableOpacity
394
+ accessibilityRole="button"
395
+ accessibilityLabel={t('call.action.expand')}
396
+ onPress={onToggleMinimize}
397
+ style={[styles.miniButton, { backgroundColor: CONTROL_IDLE }]}
398
+ >
399
+ <ExpandIcon color={TEXT_ON_DARK} size={20} />
400
+ </TouchableOpacity>
401
+ <TouchableOpacity
402
+ accessibilityRole="button"
403
+ accessibilityLabel={t('call.action.hangUp')}
404
+ onPress={onHangup}
405
+ style={[styles.miniButton, { backgroundColor: DANGER }]}
406
+ >
407
+ {icons?.hangup ?? <HangUpIcon color={TEXT_ON_DARK} size={20} />}
408
+ </TouchableOpacity>
409
+ </View>
410
+ );
411
+ }
412
+
413
+ return (
414
+ <View style={styles.canvas}>
415
+ {!!deviceError && (
416
+ <TouchableOpacity
417
+ style={styles.deviceErrorBanner}
418
+ onPress={() => setDeviceError(null)}
419
+ accessibilityRole="button"
420
+ >
421
+ <Text style={styles.deviceErrorText} numberOfLines={2}>
422
+ {deviceError}
423
+ </Text>
424
+ </TouchableOpacity>
425
+ )}
426
+ {/* Stage */}
427
+ <View style={styles.stage}>
428
+ {showRemoteVideo ? (
429
+ <VideoTrack trackRef={remoteVideo} style={styles.remoteVideo} objectFit="cover" />
430
+ ) : (
431
+ <View style={styles.centered}>
432
+ <ProfileImagePlaceholder name={displayName} size={120} />
433
+ <Text style={styles.stageName}>{displayName}</Text>
434
+ <Text style={styles.stageStatus}>
435
+ {!connected
436
+ ? t('call.status.connecting')
437
+ : !remoteJoined
438
+ ? t('call.status.waitingForPeer')
439
+ : isVideo
440
+ ? t('call.status.cameraOff')
441
+ : t('call.status.inCall')}
442
+ </Text>
443
+ {!connected && (
444
+ <ActivityIndicator
445
+ color={primaryColor}
446
+ style={styles.spinner}
447
+ size="small"
448
+ />
449
+ )}
450
+ </View>
451
+ )}
452
+
453
+ {/* Local preview, picture-in-picture */}
454
+ {isVideo && cameraOn && localVideo && (
455
+ <View style={styles.pip}>
456
+ <VideoTrack trackRef={localVideo} style={styles.pipVideo} objectFit="cover" mirror />
457
+ </View>
458
+ )}
459
+
460
+ {onToggleMinimize && (
461
+ <TouchableOpacity
462
+ accessibilityRole="button"
463
+ accessibilityLabel={t('call.action.minimize')}
464
+ onPress={onToggleMinimize}
465
+ style={styles.minimizeButton}
466
+ >
467
+ <MinimizeIcon color={TEXT_ON_DARK} size={20} />
468
+ </TouchableOpacity>
469
+ )}
470
+ </View>
471
+
472
+ {/* Control bar */}
473
+ <View style={styles.controls}>
474
+ <TouchableOpacity
475
+ accessibilityRole="button"
476
+ accessibilityLabel={micOn ? t('call.action.muteMic') : t('call.action.unmuteMic')}
477
+ onPress={toggleMic}
478
+ style={[
479
+ styles.controlButton,
480
+ { backgroundColor: micOn ? CONTROL_IDLE : DANGER },
481
+ ]}
482
+ >
483
+ {micOn
484
+ ? (icons?.micOn ?? <MicOnIcon color={TEXT_ON_DARK} />)
485
+ : (icons?.micOff ?? <MicOffIcon color={TEXT_ON_DARK} />)}
486
+ </TouchableOpacity>
487
+
488
+ {isVideo && (
489
+ <TouchableOpacity
490
+ accessibilityRole="button"
491
+ accessibilityLabel={
492
+ cameraOn ? t('call.action.stopCamera') : t('call.action.startCamera')
493
+ }
494
+ onPress={toggleCamera}
495
+ style={[
496
+ styles.controlButton,
497
+ { backgroundColor: cameraOn ? CONTROL_IDLE : DANGER },
498
+ ]}
499
+ >
500
+ {cameraOn
501
+ ? (icons?.cameraOn ?? <CameraOnIcon color={TEXT_ON_DARK} />)
502
+ : (icons?.cameraOff ?? <CameraOffIcon color={TEXT_ON_DARK} />)}
503
+ </TouchableOpacity>
504
+ )}
505
+
506
+ {isVideo && cameraOn && (
507
+ <TouchableOpacity
508
+ accessibilityRole="button"
509
+ accessibilityLabel={t('call.action.switchCamera')}
510
+ onPress={switchCamera}
511
+ style={[styles.controlButton, { backgroundColor: CONTROL_IDLE }]}
512
+ >
513
+ <SwitchCameraIcon color={TEXT_ON_DARK} />
514
+ </TouchableOpacity>
515
+ )}
516
+
517
+ <TouchableOpacity
518
+ accessibilityRole="button"
519
+ accessibilityLabel={
520
+ speakerOn ? t('call.action.speakerOff') : t('call.action.speakerOn')
521
+ }
522
+ onPress={toggleSpeaker}
523
+ style={[styles.controlButton, { backgroundColor: CONTROL_IDLE }]}
524
+ >
525
+ {speakerOn ? (
526
+ <SpeakerIcon color={TEXT_ON_DARK} />
527
+ ) : (
528
+ <SpeakerOffIcon color={TEXT_ON_DARK} />
529
+ )}
530
+ </TouchableOpacity>
531
+
532
+ <TouchableOpacity
533
+ accessibilityRole="button"
534
+ accessibilityLabel={t('call.action.hangUp')}
535
+ onPress={onHangup}
536
+ style={[styles.controlButton, styles.hangupButton]}
537
+ >
538
+ {icons?.hangup ?? <HangUpIcon color={TEXT_ON_DARK} />}
539
+ </TouchableOpacity>
540
+ </View>
541
+ </View>
542
+ );
543
+ };
544
+
545
+ const styles = StyleSheet.create({
546
+ canvas: {
547
+ flex: 1,
548
+ backgroundColor: SURFACE_DARK,
549
+ },
550
+ centered: {
551
+ flex: 1,
552
+ alignItems: 'center',
553
+ justifyContent: 'center',
554
+ gap: 12,
555
+ },
556
+ stage: {
557
+ flex: 1,
558
+ position: 'relative',
559
+ overflow: 'hidden',
560
+ },
561
+ remoteVideo: {
562
+ flex: 1,
563
+ backgroundColor: '#000',
564
+ },
565
+ stageName: {
566
+ color: TEXT_ON_DARK,
567
+ fontSize: 22,
568
+ marginTop: 16,
569
+ },
570
+ stageStatus: {
571
+ color: TEXT_MUTED,
572
+ fontSize: 14,
573
+ },
574
+ spinner: {
575
+ marginTop: 8,
576
+ },
577
+ pip: {
578
+ position: 'absolute',
579
+ right: 16,
580
+ top: 16,
581
+ width: 104,
582
+ height: 148,
583
+ borderRadius: 12,
584
+ overflow: 'hidden',
585
+ backgroundColor: '#000',
586
+ borderWidth: 1,
587
+ borderColor: 'rgba(255,255,255,0.18)',
588
+ },
589
+ pipVideo: {
590
+ flex: 1,
591
+ },
592
+ minimizeButton: {
593
+ position: 'absolute',
594
+ left: 16,
595
+ top: 16,
596
+ width: 36,
597
+ height: 36,
598
+ borderRadius: 18,
599
+ alignItems: 'center',
600
+ justifyContent: 'center',
601
+ backgroundColor: 'rgba(0,0,0,0.45)',
602
+ },
603
+ controls: {
604
+ flexDirection: 'row',
605
+ alignItems: 'center',
606
+ justifyContent: 'center',
607
+ gap: 16,
608
+ paddingVertical: 20,
609
+ paddingHorizontal: 16,
610
+ },
611
+ controlButton: {
612
+ width: 56,
613
+ height: 56,
614
+ borderRadius: 28,
615
+ alignItems: 'center',
616
+ justifyContent: 'center',
617
+ },
618
+ hangupButton: {
619
+ backgroundColor: DANGER,
620
+ },
621
+ errorText: {
622
+ color: TEXT_ON_DARK,
623
+ fontSize: 14,
624
+ textAlign: 'center',
625
+ paddingHorizontal: 24,
626
+ },
627
+ // Dismissible, non-blocking — the call is still connecting/connected
628
+ // underneath; this only flags that camera/mic didn't come up.
629
+ deviceErrorBanner: {
630
+ position: 'absolute',
631
+ top: 16,
632
+ left: 16,
633
+ right: 16,
634
+ zIndex: 10,
635
+ backgroundColor: 'rgba(229, 57, 53, 0.92)',
636
+ borderRadius: 12,
637
+ paddingVertical: 10,
638
+ paddingHorizontal: 14,
639
+ },
640
+ deviceErrorText: {
641
+ color: TEXT_ON_DARK,
642
+ fontSize: 13,
643
+ textAlign: 'center',
644
+ },
645
+ miniBar: {
646
+ flexDirection: 'row',
647
+ alignItems: 'center',
648
+ gap: 12,
649
+ paddingHorizontal: 16,
650
+ paddingVertical: 12,
651
+ borderRadius: 16,
652
+ backgroundColor: SURFACE_DARK,
653
+ },
654
+ miniInfo: {
655
+ flex: 1,
656
+ minWidth: 0,
657
+ },
658
+ miniName: {
659
+ color: TEXT_ON_DARK,
660
+ fontSize: 15,
661
+ },
662
+ miniStatus: {
663
+ color: TEXT_MUTED,
664
+ fontSize: 12,
665
+ },
666
+ miniButton: {
667
+ width: 40,
668
+ height: 40,
669
+ borderRadius: 20,
670
+ alignItems: 'center',
671
+ justifyContent: 'center',
672
+ },
673
+ });
674
+
675
+ export default VideoCallSession;