@ethora/chat-component-rn 26.9.0 → 26.10.0

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 (761) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +19 -0
  3. package/lib/commonjs/assets/icons.js +2 -2
  4. package/lib/commonjs/assets/icons.js.map +1 -1
  5. package/lib/commonjs/components/Header/HeaderRoomList.js +22 -6
  6. package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
  7. package/lib/commonjs/components/InputComponents/Search.js +38 -1
  8. package/lib/commonjs/components/InputComponents/Search.js.map +1 -1
  9. package/lib/commonjs/components/MainComponents/ArchivedMessageCard.js +166 -0
  10. package/lib/commonjs/components/MainComponents/ArchivedMessageCard.js.map +1 -0
  11. package/lib/commonjs/components/MainComponents/ChatHeader.js +22 -5
  12. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  13. package/lib/commonjs/components/MainComponents/ChatRoom.js +56 -25
  14. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  15. package/lib/commonjs/components/MainComponents/ChatWrapper.js +53 -4
  16. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  17. package/lib/commonjs/components/MainComponents/MediaMessage.js +18 -3
  18. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  19. package/lib/commonjs/components/MainComponents/MessageList.js +739 -42
  20. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  21. package/lib/commonjs/components/MainComponents/MessageSearchButton.js +58 -0
  22. package/lib/commonjs/components/MainComponents/MessageSearchButton.js.map +1 -0
  23. package/lib/commonjs/components/MainComponents/ReduxWrapper.js +15 -6
  24. package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
  25. package/lib/commonjs/components/MainComponents/RoomList.js +110 -1
  26. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  27. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +14 -3
  28. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
  29. package/lib/commonjs/components/MessageBubble/BubbleHighlight.js +112 -0
  30. package/lib/commonjs/components/MessageBubble/BubbleHighlight.js.map +1 -0
  31. package/lib/commonjs/components/MessageBubble/Message.js +169 -125
  32. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  33. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +98 -78
  34. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  35. package/lib/commonjs/components/MessageBubble/messageMenuLayout.js +50 -0
  36. package/lib/commonjs/components/MessageBubble/messageMenuLayout.js.map +1 -0
  37. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +73 -37
  38. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  39. package/lib/commonjs/components/Modals/MessageSearchModal/MessageHitResults.js +253 -0
  40. package/lib/commonjs/components/Modals/MessageSearchModal/MessageHitResults.js.map +1 -0
  41. package/lib/commonjs/components/Modals/MessageSearchModal/MessageSearchModal.js +488 -0
  42. package/lib/commonjs/components/Modals/MessageSearchModal/MessageSearchModal.js.map +1 -0
  43. package/lib/commonjs/components/Modals/MessageSearchModal/openHit.js +51 -0
  44. package/lib/commonjs/components/Modals/MessageSearchModal/openHit.js.map +1 -0
  45. package/lib/commonjs/components/Modals/MessageSearchModal/resolveSender.js +47 -0
  46. package/lib/commonjs/components/Modals/MessageSearchModal/resolveSender.js.map +1 -0
  47. package/lib/commonjs/components/Modals/MessageSearchModal/searchFilters.js +83 -0
  48. package/lib/commonjs/components/Modals/MessageSearchModal/searchFilters.js.map +1 -0
  49. package/lib/commonjs/components/Modals/MessageSearchModal/snippet.js +61 -0
  50. package/lib/commonjs/components/Modals/MessageSearchModal/snippet.js.map +1 -0
  51. package/lib/commonjs/components/Modals/MessageSearchModal/useMessageSearch.js +136 -0
  52. package/lib/commonjs/components/Modals/MessageSearchModal/useMessageSearch.js.map +1 -0
  53. package/lib/commonjs/components/Modals/PublicChatsModal/PublicChatsModal.js +306 -0
  54. package/lib/commonjs/components/Modals/PublicChatsModal/PublicChatsModal.js.map +1 -0
  55. package/lib/commonjs/components/Modals/PublicChatsModal/publicChatsLogic.js +36 -0
  56. package/lib/commonjs/components/Modals/PublicChatsModal/publicChatsLogic.js.map +1 -0
  57. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js +8 -1
  58. package/lib/commonjs/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  59. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +13 -2
  60. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  61. package/lib/commonjs/components/RoomComponents/SwipeableRoomRow.js +38 -6
  62. package/lib/commonjs/components/RoomComponents/SwipeableRoomRow.js.map +1 -1
  63. package/lib/commonjs/components/Thread/ThreadHeader.js +3 -1
  64. package/lib/commonjs/components/Thread/ThreadHeader.js.map +1 -1
  65. package/lib/commonjs/components/Thread/ThreadWrapper.js +90 -10
  66. package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
  67. package/lib/commonjs/components/styled/AudioDecoderHost.js +338 -0
  68. package/lib/commonjs/components/styled/AudioDecoderHost.js.map +1 -0
  69. package/lib/commonjs/components/styled/AudioMessage.js +493 -420
  70. package/lib/commonjs/components/styled/AudioMessage.js.map +1 -1
  71. package/lib/commonjs/components/styled/SendInput.js +31 -5
  72. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  73. package/lib/commonjs/context/MessageNotificationContext.js +14 -4
  74. package/lib/commonjs/context/MessageNotificationContext.js.map +1 -1
  75. package/lib/commonjs/context/xmppProvider.js +54 -79
  76. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  77. package/lib/commonjs/helpers/audioWaveform.js +165 -0
  78. package/lib/commonjs/helpers/audioWaveform.js.map +1 -0
  79. package/lib/commonjs/helpers/backgroundJoinSweep.js +36 -0
  80. package/lib/commonjs/helpers/backgroundJoinSweep.js.map +1 -0
  81. package/lib/commonjs/helpers/bubbleHighlight.js +44 -0
  82. package/lib/commonjs/helpers/bubbleHighlight.js.map +1 -0
  83. package/lib/commonjs/helpers/checkUniqueUsers.js +14 -12
  84. package/lib/commonjs/helpers/checkUniqueUsers.js.map +1 -1
  85. package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js +5 -1
  86. package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
  87. package/lib/commonjs/helpers/constants/MODAL_TYPES.js +3 -1
  88. package/lib/commonjs/helpers/constants/MODAL_TYPES.js.map +1 -1
  89. package/lib/commonjs/helpers/createRoomFromApi.js +40 -3
  90. package/lib/commonjs/helpers/createRoomFromApi.js.map +1 -1
  91. package/lib/commonjs/helpers/dedupeMembers.js +34 -0
  92. package/lib/commonjs/helpers/dedupeMembers.js.map +1 -0
  93. package/lib/commonjs/helpers/ethoraLogger.js +56 -0
  94. package/lib/commonjs/helpers/ethoraLogger.js.map +1 -0
  95. package/lib/commonjs/helpers/getUnnamedUsers.js +9 -4
  96. package/lib/commonjs/helpers/getUnnamedUsers.js.map +1 -1
  97. package/lib/commonjs/helpers/historyPreloadConfig.js +44 -0
  98. package/lib/commonjs/helpers/historyPreloadConfig.js.map +1 -0
  99. package/lib/commonjs/helpers/historyPreloadScheduler.js +372 -137
  100. package/lib/commonjs/helpers/historyPreloadScheduler.js.map +1 -1
  101. package/lib/commonjs/helpers/initRoomsPresence.js +17 -5
  102. package/lib/commonjs/helpers/initRoomsPresence.js.map +1 -1
  103. package/lib/commonjs/helpers/insertMessageWithDelimiter.js +5 -0
  104. package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
  105. package/lib/commonjs/helpers/isLikelyMucJid.js +67 -0
  106. package/lib/commonjs/helpers/isLikelyMucJid.js.map +1 -0
  107. package/lib/commonjs/helpers/isMessageSearchEnabled.js +18 -0
  108. package/lib/commonjs/helpers/isMessageSearchEnabled.js.map +1 -0
  109. package/lib/commonjs/helpers/joiningRoom.js +14 -0
  110. package/lib/commonjs/helpers/joiningRoom.js.map +1 -0
  111. package/lib/commonjs/helpers/jumpThread.js +100 -0
  112. package/lib/commonjs/helpers/jumpThread.js.map +1 -0
  113. package/lib/commonjs/helpers/jumpToMessage.js +58 -0
  114. package/lib/commonjs/helpers/jumpToMessage.js.map +1 -0
  115. package/lib/commonjs/helpers/jumpWindow.js +187 -0
  116. package/lib/commonjs/helpers/jumpWindow.js.map +1 -0
  117. package/lib/commonjs/helpers/loadRoomHistory.js +75 -0
  118. package/lib/commonjs/helpers/loadRoomHistory.js.map +1 -0
  119. package/lib/commonjs/helpers/notificationPolicy.js +77 -0
  120. package/lib/commonjs/helpers/notificationPolicy.js.map +1 -0
  121. package/lib/commonjs/helpers/openRoomAtMessage.js +47 -0
  122. package/lib/commonjs/helpers/openRoomAtMessage.js.map +1 -0
  123. package/lib/commonjs/helpers/openThreadForJump.js +72 -0
  124. package/lib/commonjs/helpers/openThreadForJump.js.map +1 -0
  125. package/lib/commonjs/helpers/parseMessageReference.js +27 -8
  126. package/lib/commonjs/helpers/parseMessageReference.js.map +1 -1
  127. package/lib/commonjs/helpers/publicChatsEntry.js +17 -0
  128. package/lib/commonjs/helpers/publicChatsEntry.js.map +1 -0
  129. package/lib/commonjs/helpers/roomActivityScore.js +37 -0
  130. package/lib/commonjs/helpers/roomActivityScore.js.map +1 -0
  131. package/lib/commonjs/helpers/roomUserCount.js +36 -0
  132. package/lib/commonjs/helpers/roomUserCount.js.map +1 -0
  133. package/lib/commonjs/helpers/runConfiguredHistoryPreload.js +68 -0
  134. package/lib/commonjs/helpers/runConfiguredHistoryPreload.js.map +1 -0
  135. package/lib/commonjs/helpers/sessionAppId.js +24 -0
  136. package/lib/commonjs/helpers/sessionAppId.js.map +1 -0
  137. package/lib/commonjs/helpers/timestamp.js +43 -0
  138. package/lib/commonjs/helpers/timestamp.js.map +1 -0
  139. package/lib/commonjs/helpers/userResolver.js +517 -0
  140. package/lib/commonjs/helpers/userResolver.js.map +1 -0
  141. package/lib/commonjs/helpers/xmppIdShape.js +52 -0
  142. package/lib/commonjs/helpers/xmppIdShape.js.map +1 -0
  143. package/lib/commonjs/hooks/useChatWrapperInit.js +27 -5
  144. package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
  145. package/lib/commonjs/hooks/useHeapSender.js +54 -3
  146. package/lib/commonjs/hooks/useHeapSender.js.map +1 -1
  147. package/lib/commonjs/hooks/useJumpToMessage.js +411 -0
  148. package/lib/commonjs/hooks/useJumpToMessage.js.map +1 -0
  149. package/lib/commonjs/hooks/useMessageLoaderQueue.js +32 -5
  150. package/lib/commonjs/hooks/useMessageLoaderQueue.js.map +1 -1
  151. package/lib/commonjs/hooks/useMessageQueue.js +13 -5
  152. package/lib/commonjs/hooks/useMessageQueue.js.map +1 -1
  153. package/lib/commonjs/hooks/useRoomDirectory.js +29 -0
  154. package/lib/commonjs/hooks/useRoomDirectory.js.map +1 -0
  155. package/lib/commonjs/hooks/useRoomInitialization.js +27 -5
  156. package/lib/commonjs/hooks/useRoomInitialization.js.map +1 -1
  157. package/lib/commonjs/hooks/useRoomMute.js +62 -0
  158. package/lib/commonjs/hooks/useRoomMute.js.map +1 -0
  159. package/lib/commonjs/hooks/useRoomState.js +3 -1
  160. package/lib/commonjs/hooks/useRoomState.js.map +1 -1
  161. package/lib/commonjs/hooks/useSendMessage.js +13 -3
  162. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  163. package/lib/commonjs/i18n/strings.js +318 -6
  164. package/lib/commonjs/i18n/strings.js.map +1 -1
  165. package/lib/commonjs/networking/api-requests/messageSearch.api.js +87 -0
  166. package/lib/commonjs/networking/api-requests/messageSearch.api.js.map +1 -0
  167. package/lib/commonjs/networking/api-requests/publicChats.api.js +48 -0
  168. package/lib/commonjs/networking/api-requests/publicChats.api.js.map +1 -0
  169. package/lib/commonjs/networking/api-requests/roomMembers.api.js +253 -4
  170. package/lib/commonjs/networking/api-requests/roomMembers.api.js.map +1 -1
  171. package/lib/commonjs/networking/api-requests/rooms.api.js +48 -12
  172. package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
  173. package/lib/commonjs/networking/api-requests/xmppToken.api.js +71 -0
  174. package/lib/commonjs/networking/api-requests/xmppToken.api.js.map +1 -0
  175. package/lib/commonjs/networking/ethoraEvents.js +227 -0
  176. package/lib/commonjs/networking/ethoraEvents.js.map +1 -0
  177. package/lib/commonjs/networking/stanzaHandlers.js +153 -16
  178. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  179. package/lib/commonjs/networking/xmpp/allRoomPresences.xmpp.js +124 -3
  180. package/lib/commonjs/networking/xmpp/allRoomPresences.xmpp.js.map +1 -1
  181. package/lib/commonjs/networking/xmpp/getHistory.xmpp.js +319 -35
  182. package/lib/commonjs/networking/xmpp/getHistory.xmpp.js.map +1 -1
  183. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +7 -0
  184. package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  185. package/lib/commonjs/networking/xmpp/historyCursor.js +101 -0
  186. package/lib/commonjs/networking/xmpp/historyCursor.js.map +1 -0
  187. package/lib/commonjs/networking/xmpp/mamQueryIds.js +17 -0
  188. package/lib/commonjs/networking/xmpp/mamQueryIds.js.map +1 -0
  189. package/lib/commonjs/networking/xmpp/mamRouter.js +24 -9
  190. package/lib/commonjs/networking/xmpp/mamRouter.js.map +1 -1
  191. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js +70 -65
  192. package/lib/commonjs/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  193. package/lib/commonjs/networking/xmpp/roomJoinSweep.js +389 -0
  194. package/lib/commonjs/networking/xmpp/roomJoinSweep.js.map +1 -0
  195. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js +0 -1
  196. package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  197. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +0 -1
  198. package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  199. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +0 -1
  200. package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  201. package/lib/commonjs/networking/xmppClient.js +269 -41
  202. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  203. package/lib/commonjs/networking/xmppCredentials.js +80 -0
  204. package/lib/commonjs/networking/xmppCredentials.js.map +1 -0
  205. package/lib/commonjs/roomStore/Middleware/jumpThreadMiddleware.js +158 -0
  206. package/lib/commonjs/roomStore/Middleware/jumpThreadMiddleware.js.map +1 -0
  207. package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js +6 -0
  208. package/lib/commonjs/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
  209. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js +45 -7
  210. package/lib/commonjs/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  211. package/lib/commonjs/roomStore/chatSettingsSlice.js +15 -1
  212. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  213. package/lib/commonjs/roomStore/index.js +13 -3
  214. package/lib/commonjs/roomStore/index.js.map +1 -1
  215. package/lib/commonjs/roomStore/persistence.js +48 -1
  216. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  217. package/lib/commonjs/roomStore/roomsSlice.js +474 -60
  218. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  219. package/lib/commonjs/roomStore/safeKey.js +44 -0
  220. package/lib/commonjs/roomStore/safeKey.js.map +1 -0
  221. package/lib/commonjs/roomStore/usersSetCap.js +37 -0
  222. package/lib/commonjs/roomStore/usersSetCap.js.map +1 -0
  223. package/lib/commonjs/version.js +1 -1
  224. package/lib/commonjs/version.js.map +1 -1
  225. package/lib/module/assets/icons.js +1 -1
  226. package/lib/module/assets/icons.js.map +1 -1
  227. package/lib/module/components/Header/HeaderRoomList.js +23 -7
  228. package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
  229. package/lib/module/components/InputComponents/Search.js +39 -2
  230. package/lib/module/components/InputComponents/Search.js.map +1 -1
  231. package/lib/module/components/MainComponents/ArchivedMessageCard.js +161 -0
  232. package/lib/module/components/MainComponents/ArchivedMessageCard.js.map +1 -0
  233. package/lib/module/components/MainComponents/ChatHeader.js +22 -5
  234. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  235. package/lib/module/components/MainComponents/ChatRoom.js +57 -26
  236. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  237. package/lib/module/components/MainComponents/ChatWrapper.js +54 -5
  238. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  239. package/lib/module/components/MainComponents/MediaMessage.js +18 -3
  240. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  241. package/lib/module/components/MainComponents/MessageList.js +740 -42
  242. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  243. package/lib/module/components/MainComponents/MessageSearchButton.js +48 -0
  244. package/lib/module/components/MainComponents/MessageSearchButton.js.map +1 -0
  245. package/lib/module/components/MainComponents/ReduxWrapper.js +15 -7
  246. package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
  247. package/lib/module/components/MainComponents/RoomList.js +111 -2
  248. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  249. package/lib/module/components/Menu/HeaderRoomListMenu.js +15 -4
  250. package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
  251. package/lib/module/components/MessageBubble/BubbleHighlight.js +106 -0
  252. package/lib/module/components/MessageBubble/BubbleHighlight.js.map +1 -0
  253. package/lib/module/components/MessageBubble/Message.js +172 -127
  254. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  255. package/lib/module/components/MessageBubble/MessageInteractions.js +98 -78
  256. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  257. package/lib/module/components/MessageBubble/messageMenuLayout.js +45 -0
  258. package/lib/module/components/MessageBubble/messageMenuLayout.js.map +1 -0
  259. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +74 -38
  260. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  261. package/lib/module/components/Modals/MessageSearchModal/MessageHitResults.js +243 -0
  262. package/lib/module/components/Modals/MessageSearchModal/MessageHitResults.js.map +1 -0
  263. package/lib/module/components/Modals/MessageSearchModal/MessageSearchModal.js +482 -0
  264. package/lib/module/components/Modals/MessageSearchModal/MessageSearchModal.js.map +1 -0
  265. package/lib/module/components/Modals/MessageSearchModal/openHit.js +45 -0
  266. package/lib/module/components/Modals/MessageSearchModal/openHit.js.map +1 -0
  267. package/lib/module/components/Modals/MessageSearchModal/resolveSender.js +41 -0
  268. package/lib/module/components/Modals/MessageSearchModal/resolveSender.js.map +1 -0
  269. package/lib/module/components/Modals/MessageSearchModal/searchFilters.js +74 -0
  270. package/lib/module/components/Modals/MessageSearchModal/searchFilters.js.map +1 -0
  271. package/lib/module/components/Modals/MessageSearchModal/snippet.js +57 -0
  272. package/lib/module/components/Modals/MessageSearchModal/snippet.js.map +1 -0
  273. package/lib/module/components/Modals/MessageSearchModal/useMessageSearch.js +131 -0
  274. package/lib/module/components/Modals/MessageSearchModal/useMessageSearch.js.map +1 -0
  275. package/lib/module/components/Modals/PublicChatsModal/PublicChatsModal.js +300 -0
  276. package/lib/module/components/Modals/PublicChatsModal/PublicChatsModal.js.map +1 -0
  277. package/lib/module/components/Modals/PublicChatsModal/publicChatsLogic.js +30 -0
  278. package/lib/module/components/Modals/PublicChatsModal/publicChatsLogic.js.map +1 -0
  279. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js +8 -1
  280. package/lib/module/components/Modals/SelectUsersModal/SelectUsersModal.js.map +1 -1
  281. package/lib/module/components/RoomComponents/ChatRoomItem.js +14 -3
  282. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  283. package/lib/module/components/RoomComponents/SwipeableRoomRow.js +39 -7
  284. package/lib/module/components/RoomComponents/SwipeableRoomRow.js.map +1 -1
  285. package/lib/module/components/Thread/ThreadHeader.js +3 -1
  286. package/lib/module/components/Thread/ThreadHeader.js.map +1 -1
  287. package/lib/module/components/Thread/ThreadWrapper.js +89 -9
  288. package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
  289. package/lib/module/components/styled/AudioDecoderHost.js +332 -0
  290. package/lib/module/components/styled/AudioDecoderHost.js.map +1 -0
  291. package/lib/module/components/styled/AudioMessage.js +495 -422
  292. package/lib/module/components/styled/AudioMessage.js.map +1 -1
  293. package/lib/module/components/styled/SendInput.js +32 -6
  294. package/lib/module/components/styled/SendInput.js.map +1 -1
  295. package/lib/module/context/MessageNotificationContext.js +15 -5
  296. package/lib/module/context/MessageNotificationContext.js.map +1 -1
  297. package/lib/module/context/xmppProvider.js +54 -79
  298. package/lib/module/context/xmppProvider.js.map +1 -1
  299. package/lib/module/helpers/audioWaveform.js +153 -0
  300. package/lib/module/helpers/audioWaveform.js.map +1 -0
  301. package/lib/module/helpers/backgroundJoinSweep.js +30 -0
  302. package/lib/module/helpers/backgroundJoinSweep.js.map +1 -0
  303. package/lib/module/helpers/bubbleHighlight.js +38 -0
  304. package/lib/module/helpers/bubbleHighlight.js.map +1 -0
  305. package/lib/module/helpers/checkUniqueUsers.js +15 -13
  306. package/lib/module/helpers/checkUniqueUsers.js.map +1 -1
  307. package/lib/module/helpers/constants/MODAL_COMPONENTS.js +5 -1
  308. package/lib/module/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
  309. package/lib/module/helpers/constants/MODAL_TYPES.js +3 -1
  310. package/lib/module/helpers/constants/MODAL_TYPES.js.map +1 -1
  311. package/lib/module/helpers/createRoomFromApi.js +37 -3
  312. package/lib/module/helpers/createRoomFromApi.js.map +1 -1
  313. package/lib/module/helpers/dedupeMembers.js +28 -0
  314. package/lib/module/helpers/dedupeMembers.js.map +1 -0
  315. package/lib/module/helpers/ethoraLogger.js +52 -0
  316. package/lib/module/helpers/ethoraLogger.js.map +1 -0
  317. package/lib/module/helpers/getUnnamedUsers.js +9 -4
  318. package/lib/module/helpers/getUnnamedUsers.js.map +1 -1
  319. package/lib/module/helpers/historyPreloadConfig.js +39 -0
  320. package/lib/module/helpers/historyPreloadConfig.js.map +1 -0
  321. package/lib/module/helpers/historyPreloadScheduler.js +368 -134
  322. package/lib/module/helpers/historyPreloadScheduler.js.map +1 -1
  323. package/lib/module/helpers/initRoomsPresence.js +18 -5
  324. package/lib/module/helpers/initRoomsPresence.js.map +1 -1
  325. package/lib/module/helpers/insertMessageWithDelimiter.js +5 -0
  326. package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
  327. package/lib/module/helpers/isLikelyMucJid.js +60 -0
  328. package/lib/module/helpers/isLikelyMucJid.js.map +1 -0
  329. package/lib/module/helpers/isMessageSearchEnabled.js +13 -0
  330. package/lib/module/helpers/isMessageSearchEnabled.js.map +1 -0
  331. package/lib/module/helpers/joiningRoom.js +9 -0
  332. package/lib/module/helpers/joiningRoom.js.map +1 -0
  333. package/lib/module/helpers/jumpThread.js +83 -0
  334. package/lib/module/helpers/jumpThread.js.map +1 -0
  335. package/lib/module/helpers/jumpToMessage.js +52 -0
  336. package/lib/module/helpers/jumpToMessage.js.map +1 -0
  337. package/lib/module/helpers/jumpWindow.js +177 -0
  338. package/lib/module/helpers/jumpWindow.js.map +1 -0
  339. package/lib/module/helpers/loadRoomHistory.js +69 -0
  340. package/lib/module/helpers/loadRoomHistory.js.map +1 -0
  341. package/lib/module/helpers/notificationPolicy.js +70 -0
  342. package/lib/module/helpers/notificationPolicy.js.map +1 -0
  343. package/lib/module/helpers/openRoomAtMessage.js +41 -0
  344. package/lib/module/helpers/openRoomAtMessage.js.map +1 -0
  345. package/lib/module/helpers/openThreadForJump.js +68 -0
  346. package/lib/module/helpers/openThreadForJump.js.map +1 -0
  347. package/lib/module/helpers/parseMessageReference.js +26 -6
  348. package/lib/module/helpers/parseMessageReference.js.map +1 -1
  349. package/lib/module/helpers/publicChatsEntry.js +12 -0
  350. package/lib/module/helpers/publicChatsEntry.js.map +1 -0
  351. package/lib/module/helpers/roomActivityScore.js +30 -0
  352. package/lib/module/helpers/roomActivityScore.js.map +1 -0
  353. package/lib/module/helpers/roomUserCount.js +29 -0
  354. package/lib/module/helpers/roomUserCount.js.map +1 -0
  355. package/lib/module/helpers/runConfiguredHistoryPreload.js +63 -0
  356. package/lib/module/helpers/runConfiguredHistoryPreload.js.map +1 -0
  357. package/lib/module/helpers/sessionAppId.js +20 -0
  358. package/lib/module/helpers/sessionAppId.js.map +1 -0
  359. package/lib/module/helpers/timestamp.js +37 -0
  360. package/lib/module/helpers/timestamp.js.map +1 -0
  361. package/lib/module/helpers/userResolver.js +503 -0
  362. package/lib/module/helpers/userResolver.js.map +1 -0
  363. package/lib/module/helpers/xmppIdShape.js +46 -0
  364. package/lib/module/helpers/xmppIdShape.js.map +1 -0
  365. package/lib/module/hooks/useChatWrapperInit.js +16 -5
  366. package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
  367. package/lib/module/hooks/useHeapSender.js +54 -3
  368. package/lib/module/hooks/useHeapSender.js.map +1 -1
  369. package/lib/module/hooks/useJumpToMessage.js +389 -0
  370. package/lib/module/hooks/useJumpToMessage.js.map +1 -0
  371. package/lib/module/hooks/useMessageLoaderQueue.js +32 -5
  372. package/lib/module/hooks/useMessageLoaderQueue.js.map +1 -1
  373. package/lib/module/hooks/useMessageQueue.js +13 -5
  374. package/lib/module/hooks/useMessageQueue.js.map +1 -1
  375. package/lib/module/hooks/useRoomDirectory.js +24 -0
  376. package/lib/module/hooks/useRoomDirectory.js.map +1 -0
  377. package/lib/module/hooks/useRoomInitialization.js +29 -7
  378. package/lib/module/hooks/useRoomInitialization.js.map +1 -1
  379. package/lib/module/hooks/useRoomMute.js +58 -0
  380. package/lib/module/hooks/useRoomMute.js.map +1 -0
  381. package/lib/module/hooks/useRoomState.js +3 -1
  382. package/lib/module/hooks/useRoomState.js.map +1 -1
  383. package/lib/module/hooks/useSendMessage.js +13 -3
  384. package/lib/module/hooks/useSendMessage.js.map +1 -1
  385. package/lib/module/i18n/strings.js +318 -6
  386. package/lib/module/i18n/strings.js.map +1 -1
  387. package/lib/module/networking/api-requests/messageSearch.api.js +81 -0
  388. package/lib/module/networking/api-requests/messageSearch.api.js.map +1 -0
  389. package/lib/module/networking/api-requests/publicChats.api.js +43 -0
  390. package/lib/module/networking/api-requests/publicChats.api.js.map +1 -0
  391. package/lib/module/networking/api-requests/roomMembers.api.js +248 -4
  392. package/lib/module/networking/api-requests/roomMembers.api.js.map +1 -1
  393. package/lib/module/networking/api-requests/rooms.api.js +48 -12
  394. package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
  395. package/lib/module/networking/api-requests/xmppToken.api.js +66 -0
  396. package/lib/module/networking/api-requests/xmppToken.api.js.map +1 -0
  397. package/lib/module/networking/ethoraEvents.js +220 -0
  398. package/lib/module/networking/ethoraEvents.js.map +1 -0
  399. package/lib/module/networking/stanzaHandlers.js +155 -18
  400. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  401. package/lib/module/networking/xmpp/allRoomPresences.xmpp.js +125 -3
  402. package/lib/module/networking/xmpp/allRoomPresences.xmpp.js.map +1 -1
  403. package/lib/module/networking/xmpp/getHistory.xmpp.js +318 -35
  404. package/lib/module/networking/xmpp/getHistory.xmpp.js.map +1 -1
  405. package/lib/module/networking/xmpp/handleStanzas.xmpp.js +7 -0
  406. package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
  407. package/lib/module/networking/xmpp/historyCursor.js +95 -0
  408. package/lib/module/networking/xmpp/historyCursor.js.map +1 -0
  409. package/lib/module/networking/xmpp/mamQueryIds.js +12 -0
  410. package/lib/module/networking/xmpp/mamQueryIds.js.map +1 -0
  411. package/lib/module/networking/xmpp/mamRouter.js +22 -8
  412. package/lib/module/networking/xmpp/mamRouter.js.map +1 -1
  413. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js +68 -62
  414. package/lib/module/networking/xmpp/presenceInRoom.xmpp.js.map +1 -1
  415. package/lib/module/networking/xmpp/roomJoinSweep.js +385 -0
  416. package/lib/module/networking/xmpp/roomJoinSweep.js.map +1 -0
  417. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js +0 -1
  418. package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
  419. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +0 -1
  420. package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
  421. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +0 -1
  422. package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
  423. package/lib/module/networking/xmppClient.js +269 -41
  424. package/lib/module/networking/xmppClient.js.map +1 -1
  425. package/lib/module/networking/xmppCredentials.js +76 -0
  426. package/lib/module/networking/xmppCredentials.js.map +1 -0
  427. package/lib/module/roomStore/Middleware/jumpThreadMiddleware.js +153 -0
  428. package/lib/module/roomStore/Middleware/jumpThreadMiddleware.js.map +1 -0
  429. package/lib/module/roomStore/Middleware/reactionsMiddleware.js +6 -0
  430. package/lib/module/roomStore/Middleware/reactionsMiddleware.js.map +1 -1
  431. package/lib/module/roomStore/Middleware/unreadMidlleware.js +46 -7
  432. package/lib/module/roomStore/Middleware/unreadMidlleware.js.map +1 -1
  433. package/lib/module/roomStore/chatSettingsSlice.js +13 -0
  434. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  435. package/lib/module/roomStore/index.js +7 -3
  436. package/lib/module/roomStore/index.js.map +1 -1
  437. package/lib/module/roomStore/persistence.js +46 -1
  438. package/lib/module/roomStore/persistence.js.map +1 -1
  439. package/lib/module/roomStore/roomsSlice.js +459 -58
  440. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  441. package/lib/module/roomStore/safeKey.js +37 -0
  442. package/lib/module/roomStore/safeKey.js.map +1 -0
  443. package/lib/module/roomStore/usersSetCap.js +32 -0
  444. package/lib/module/roomStore/usersSetCap.js.map +1 -0
  445. package/lib/module/version.js +1 -1
  446. package/lib/module/version.js.map +1 -1
  447. package/lib/typescript/assets/icons.d.ts +4 -0
  448. package/lib/typescript/assets/icons.d.ts.map +1 -1
  449. package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
  450. package/lib/typescript/components/InputComponents/Search.d.ts.map +1 -1
  451. package/lib/typescript/components/MainComponents/ArchivedMessageCard.d.ts +12 -0
  452. package/lib/typescript/components/MainComponents/ArchivedMessageCard.d.ts.map +1 -0
  453. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  454. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  455. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  456. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  457. package/lib/typescript/components/MainComponents/MessageList.d.ts +22 -1
  458. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  459. package/lib/typescript/components/MainComponents/MessageSearchButton.d.ts +7 -0
  460. package/lib/typescript/components/MainComponents/MessageSearchButton.d.ts.map +1 -0
  461. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
  462. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  463. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
  464. package/lib/typescript/components/MessageBubble/BubbleHighlight.d.ts +12 -0
  465. package/lib/typescript/components/MessageBubble/BubbleHighlight.d.ts.map +1 -0
  466. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  467. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts +5 -0
  468. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  469. package/lib/typescript/components/MessageBubble/messageMenuLayout.d.ts +29 -0
  470. package/lib/typescript/components/MessageBubble/messageMenuLayout.d.ts.map +1 -0
  471. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts +2 -0
  472. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  473. package/lib/typescript/components/Modals/MessageSearchModal/MessageHitResults.d.ts +44 -0
  474. package/lib/typescript/components/Modals/MessageSearchModal/MessageHitResults.d.ts.map +1 -0
  475. package/lib/typescript/components/Modals/MessageSearchModal/MessageSearchModal.d.ts +12 -0
  476. package/lib/typescript/components/Modals/MessageSearchModal/MessageSearchModal.d.ts.map +1 -0
  477. package/lib/typescript/components/Modals/MessageSearchModal/openHit.d.ts +15 -0
  478. package/lib/typescript/components/Modals/MessageSearchModal/openHit.d.ts.map +1 -0
  479. package/lib/typescript/components/Modals/MessageSearchModal/resolveSender.d.ts +34 -0
  480. package/lib/typescript/components/Modals/MessageSearchModal/resolveSender.d.ts.map +1 -0
  481. package/lib/typescript/components/Modals/MessageSearchModal/searchFilters.d.ts +41 -0
  482. package/lib/typescript/components/Modals/MessageSearchModal/searchFilters.d.ts.map +1 -0
  483. package/lib/typescript/components/Modals/MessageSearchModal/snippet.d.ts +15 -0
  484. package/lib/typescript/components/Modals/MessageSearchModal/snippet.d.ts.map +1 -0
  485. package/lib/typescript/components/Modals/MessageSearchModal/useMessageSearch.d.ts +23 -0
  486. package/lib/typescript/components/Modals/MessageSearchModal/useMessageSearch.d.ts.map +1 -0
  487. package/lib/typescript/components/Modals/PublicChatsModal/PublicChatsModal.d.ts +16 -0
  488. package/lib/typescript/components/Modals/PublicChatsModal/PublicChatsModal.d.ts.map +1 -0
  489. package/lib/typescript/components/Modals/PublicChatsModal/publicChatsLogic.d.ts +14 -0
  490. package/lib/typescript/components/Modals/PublicChatsModal/publicChatsLogic.d.ts.map +1 -0
  491. package/lib/typescript/components/Modals/SelectUsersModal/SelectUsersModal.d.ts.map +1 -1
  492. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  493. package/lib/typescript/components/RoomComponents/SwipeableRoomRow.d.ts +5 -1
  494. package/lib/typescript/components/RoomComponents/SwipeableRoomRow.d.ts.map +1 -1
  495. package/lib/typescript/components/Thread/ThreadHeader.d.ts.map +1 -1
  496. package/lib/typescript/components/Thread/ThreadWrapper.d.ts +1 -0
  497. package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
  498. package/lib/typescript/components/styled/AudioDecoderHost.d.ts +39 -0
  499. package/lib/typescript/components/styled/AudioDecoderHost.d.ts.map +1 -0
  500. package/lib/typescript/components/styled/AudioMessage.d.ts +14 -1
  501. package/lib/typescript/components/styled/AudioMessage.d.ts.map +1 -1
  502. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  503. package/lib/typescript/context/MessageNotificationContext.d.ts.map +1 -1
  504. package/lib/typescript/context/xmppProvider.d.ts +2 -0
  505. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  506. package/lib/typescript/helpers/audioWaveform.d.ts +54 -0
  507. package/lib/typescript/helpers/audioWaveform.d.ts.map +1 -0
  508. package/lib/typescript/helpers/backgroundJoinSweep.d.ts +14 -0
  509. package/lib/typescript/helpers/backgroundJoinSweep.d.ts.map +1 -0
  510. package/lib/typescript/helpers/bubbleHighlight.d.ts +17 -0
  511. package/lib/typescript/helpers/bubbleHighlight.d.ts.map +1 -0
  512. package/lib/typescript/helpers/checkUniqueUsers.d.ts +2 -2
  513. package/lib/typescript/helpers/checkUniqueUsers.d.ts.map +1 -1
  514. package/lib/typescript/helpers/constants/MODAL_COMPONENTS.d.ts.map +1 -1
  515. package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts +2 -0
  516. package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts.map +1 -1
  517. package/lib/typescript/helpers/createRoomFromApi.d.ts +25 -0
  518. package/lib/typescript/helpers/createRoomFromApi.d.ts.map +1 -1
  519. package/lib/typescript/helpers/dedupeMembers.d.ts +11 -0
  520. package/lib/typescript/helpers/dedupeMembers.d.ts.map +1 -0
  521. package/lib/typescript/helpers/ethoraLogger.d.ts +13 -0
  522. package/lib/typescript/helpers/ethoraLogger.d.ts.map +1 -0
  523. package/lib/typescript/helpers/getUnnamedUsers.d.ts.map +1 -1
  524. package/lib/typescript/helpers/historyPreloadConfig.d.ts +26 -0
  525. package/lib/typescript/helpers/historyPreloadConfig.d.ts.map +1 -0
  526. package/lib/typescript/helpers/historyPreloadScheduler.d.ts +8 -4
  527. package/lib/typescript/helpers/historyPreloadScheduler.d.ts.map +1 -1
  528. package/lib/typescript/helpers/initRoomsPresence.d.ts +7 -0
  529. package/lib/typescript/helpers/initRoomsPresence.d.ts.map +1 -1
  530. package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
  531. package/lib/typescript/helpers/isLikelyMucJid.d.ts +19 -0
  532. package/lib/typescript/helpers/isLikelyMucJid.d.ts.map +1 -0
  533. package/lib/typescript/helpers/isMessageSearchEnabled.d.ts +12 -0
  534. package/lib/typescript/helpers/isMessageSearchEnabled.d.ts.map +1 -0
  535. package/lib/typescript/helpers/joiningRoom.d.ts +7 -0
  536. package/lib/typescript/helpers/joiningRoom.d.ts.map +1 -0
  537. package/lib/typescript/helpers/jumpThread.d.ts +48 -0
  538. package/lib/typescript/helpers/jumpThread.d.ts.map +1 -0
  539. package/lib/typescript/helpers/jumpToMessage.d.ts +26 -0
  540. package/lib/typescript/helpers/jumpToMessage.d.ts.map +1 -0
  541. package/lib/typescript/helpers/jumpWindow.d.ts +74 -0
  542. package/lib/typescript/helpers/jumpWindow.d.ts.map +1 -0
  543. package/lib/typescript/helpers/loadRoomHistory.d.ts +34 -0
  544. package/lib/typescript/helpers/loadRoomHistory.d.ts.map +1 -0
  545. package/lib/typescript/helpers/notificationPolicy.d.ts +42 -0
  546. package/lib/typescript/helpers/notificationPolicy.d.ts.map +1 -0
  547. package/lib/typescript/helpers/openRoomAtMessage.d.ts +17 -0
  548. package/lib/typescript/helpers/openRoomAtMessage.d.ts.map +1 -0
  549. package/lib/typescript/helpers/openThreadForJump.d.ts +27 -0
  550. package/lib/typescript/helpers/openThreadForJump.d.ts.map +1 -0
  551. package/lib/typescript/helpers/parseMessageReference.d.ts +18 -5
  552. package/lib/typescript/helpers/parseMessageReference.d.ts.map +1 -1
  553. package/lib/typescript/helpers/publicChatsEntry.d.ts +11 -0
  554. package/lib/typescript/helpers/publicChatsEntry.d.ts.map +1 -0
  555. package/lib/typescript/helpers/roomActivityScore.d.ts +5 -0
  556. package/lib/typescript/helpers/roomActivityScore.d.ts.map +1 -0
  557. package/lib/typescript/helpers/roomUserCount.d.ts +20 -0
  558. package/lib/typescript/helpers/roomUserCount.d.ts.map +1 -0
  559. package/lib/typescript/helpers/runConfiguredHistoryPreload.d.ts +21 -0
  560. package/lib/typescript/helpers/runConfiguredHistoryPreload.d.ts.map +1 -0
  561. package/lib/typescript/helpers/sessionAppId.d.ts +8 -0
  562. package/lib/typescript/helpers/sessionAppId.d.ts.map +1 -0
  563. package/lib/typescript/helpers/timestamp.d.ts +3 -0
  564. package/lib/typescript/helpers/timestamp.d.ts.map +1 -0
  565. package/lib/typescript/helpers/userResolver.d.ts +58 -0
  566. package/lib/typescript/helpers/userResolver.d.ts.map +1 -0
  567. package/lib/typescript/helpers/xmppIdShape.d.ts +10 -0
  568. package/lib/typescript/helpers/xmppIdShape.d.ts.map +1 -0
  569. package/lib/typescript/hooks/useChatWrapperInit.d.ts +2 -0
  570. package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
  571. package/lib/typescript/hooks/useHeapSender.d.ts.map +1 -1
  572. package/lib/typescript/hooks/useJumpToMessage.d.ts +116 -0
  573. package/lib/typescript/hooks/useJumpToMessage.d.ts.map +1 -0
  574. package/lib/typescript/hooks/useMessageLoaderQueue.d.ts.map +1 -1
  575. package/lib/typescript/hooks/useMessageQueue.d.ts.map +1 -1
  576. package/lib/typescript/hooks/useRoomDirectory.d.ts +12 -0
  577. package/lib/typescript/hooks/useRoomDirectory.d.ts.map +1 -0
  578. package/lib/typescript/hooks/useRoomInitialization.d.ts.map +1 -1
  579. package/lib/typescript/hooks/useRoomMute.d.ts +8 -0
  580. package/lib/typescript/hooks/useRoomMute.d.ts.map +1 -0
  581. package/lib/typescript/hooks/useRoomState.d.ts +1 -0
  582. package/lib/typescript/hooks/useRoomState.d.ts.map +1 -1
  583. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  584. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  585. package/lib/typescript/networking/api-requests/messageSearch.api.d.ts +57 -0
  586. package/lib/typescript/networking/api-requests/messageSearch.api.d.ts.map +1 -0
  587. package/lib/typescript/networking/api-requests/publicChats.api.d.ts +24 -0
  588. package/lib/typescript/networking/api-requests/publicChats.api.d.ts.map +1 -0
  589. package/lib/typescript/networking/api-requests/roomMembers.api.d.ts +19 -1
  590. package/lib/typescript/networking/api-requests/roomMembers.api.d.ts.map +1 -1
  591. package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
  592. package/lib/typescript/networking/api-requests/xmppToken.api.d.ts +2 -0
  593. package/lib/typescript/networking/api-requests/xmppToken.api.d.ts.map +1 -0
  594. package/lib/typescript/networking/ethoraEvents.d.ts +45 -0
  595. package/lib/typescript/networking/ethoraEvents.d.ts.map +1 -0
  596. package/lib/typescript/networking/stanzaHandlers.d.ts +1 -1
  597. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  598. package/lib/typescript/networking/xmpp/allRoomPresences.xmpp.d.ts +32 -1
  599. package/lib/typescript/networking/xmpp/allRoomPresences.xmpp.d.ts.map +1 -1
  600. package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts +64 -0
  601. package/lib/typescript/networking/xmpp/getHistory.xmpp.d.ts.map +1 -1
  602. package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
  603. package/lib/typescript/networking/xmpp/historyCursor.d.ts +35 -0
  604. package/lib/typescript/networking/xmpp/historyCursor.d.ts.map +1 -0
  605. package/lib/typescript/networking/xmpp/mamQueryIds.d.ts +10 -0
  606. package/lib/typescript/networking/xmpp/mamQueryIds.d.ts.map +1 -0
  607. package/lib/typescript/networking/xmpp/mamRouter.d.ts +7 -0
  608. package/lib/typescript/networking/xmpp/mamRouter.d.ts.map +1 -1
  609. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts +13 -2
  610. package/lib/typescript/networking/xmpp/presenceInRoom.xmpp.d.ts.map +1 -1
  611. package/lib/typescript/networking/xmpp/roomJoinSweep.d.ts +82 -0
  612. package/lib/typescript/networking/xmpp/roomJoinSweep.d.ts.map +1 -0
  613. package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts.map +1 -1
  614. package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
  615. package/lib/typescript/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts.map +1 -1
  616. package/lib/typescript/networking/xmppClient.d.ts +90 -4
  617. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  618. package/lib/typescript/networking/xmppCredentials.d.ts +12 -0
  619. package/lib/typescript/networking/xmppCredentials.d.ts.map +1 -0
  620. package/lib/typescript/roomStore/Middleware/jumpThreadMiddleware.d.ts +3 -0
  621. package/lib/typescript/roomStore/Middleware/jumpThreadMiddleware.d.ts.map +1 -0
  622. package/lib/typescript/roomStore/Middleware/reactionsMiddleware.d.ts.map +1 -1
  623. package/lib/typescript/roomStore/Middleware/unreadMidlleware.d.ts.map +1 -1
  624. package/lib/typescript/roomStore/chatSettingsSlice.d.ts +8 -1
  625. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  626. package/lib/typescript/roomStore/index.d.ts +2 -1
  627. package/lib/typescript/roomStore/index.d.ts.map +1 -1
  628. package/lib/typescript/roomStore/persistence.d.ts +8 -0
  629. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  630. package/lib/typescript/roomStore/roomsSlice.d.ts +138 -2
  631. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  632. package/lib/typescript/roomStore/safeKey.d.ts +13 -0
  633. package/lib/typescript/roomStore/safeKey.d.ts.map +1 -0
  634. package/lib/typescript/roomStore/usersSetCap.d.ts +16 -0
  635. package/lib/typescript/roomStore/usersSetCap.d.ts.map +1 -0
  636. package/lib/typescript/types/models/media.model.d.ts +2 -0
  637. package/lib/typescript/types/models/media.model.d.ts.map +1 -1
  638. package/lib/typescript/types/models/room.model.d.ts +18 -5
  639. package/lib/typescript/types/models/room.model.d.ts.map +1 -1
  640. package/lib/typescript/types/models/xmpp.model.d.ts +8 -1
  641. package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
  642. package/lib/typescript/types/types.d.ts +90 -5
  643. package/lib/typescript/types/types.d.ts.map +1 -1
  644. package/lib/typescript/version.d.ts +1 -1
  645. package/lib/typescript/version.d.ts.map +1 -1
  646. package/package.json +1 -1
  647. package/src/assets/icons.tsx +13 -0
  648. package/src/components/Header/HeaderRoomList.tsx +21 -4
  649. package/src/components/InputComponents/Search.tsx +33 -0
  650. package/src/components/MainComponents/ArchivedMessageCard.tsx +139 -0
  651. package/src/components/MainComponents/ChatHeader.tsx +19 -3
  652. package/src/components/MainComponents/ChatRoom.tsx +60 -23
  653. package/src/components/MainComponents/ChatWrapper.tsx +50 -9
  654. package/src/components/MainComponents/MediaMessage.tsx +17 -0
  655. package/src/components/MainComponents/MessageList.tsx +823 -84
  656. package/src/components/MainComponents/MessageSearchButton.tsx +48 -0
  657. package/src/components/MainComponents/ReduxWrapper.tsx +16 -5
  658. package/src/components/MainComponents/RoomList.tsx +125 -0
  659. package/src/components/Menu/HeaderRoomListMenu.tsx +26 -2
  660. package/src/components/MessageBubble/BubbleHighlight.tsx +106 -0
  661. package/src/components/MessageBubble/Message.tsx +265 -177
  662. package/src/components/MessageBubble/MessageInteractions.tsx +109 -86
  663. package/src/components/MessageBubble/messageMenuLayout.ts +59 -0
  664. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +92 -21
  665. package/src/components/Modals/MessageSearchModal/MessageHitResults.tsx +271 -0
  666. package/src/components/Modals/MessageSearchModal/MessageSearchModal.tsx +487 -0
  667. package/src/components/Modals/MessageSearchModal/openHit.ts +50 -0
  668. package/src/components/Modals/MessageSearchModal/resolveSender.ts +57 -0
  669. package/src/components/Modals/MessageSearchModal/searchFilters.ts +109 -0
  670. package/src/components/Modals/MessageSearchModal/snippet.ts +58 -0
  671. package/src/components/Modals/MessageSearchModal/useMessageSearch.ts +151 -0
  672. package/src/components/Modals/PublicChatsModal/PublicChatsModal.tsx +288 -0
  673. package/src/components/Modals/PublicChatsModal/publicChatsLogic.ts +36 -0
  674. package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +8 -1
  675. package/src/components/RoomComponents/ChatRoomItem.tsx +16 -3
  676. package/src/components/RoomComponents/SwipeableRoomRow.tsx +39 -6
  677. package/src/components/Thread/ThreadHeader.tsx +3 -1
  678. package/src/components/Thread/ThreadWrapper.tsx +96 -10
  679. package/src/components/styled/AudioDecoderHost.tsx +351 -0
  680. package/src/components/styled/AudioMessage.tsx +482 -425
  681. package/src/components/styled/SendInput.tsx +42 -4
  682. package/src/context/MessageNotificationContext.tsx +19 -5
  683. package/src/context/xmppProvider.tsx +56 -91
  684. package/src/helpers/audioWaveform.ts +142 -0
  685. package/src/helpers/backgroundJoinSweep.ts +35 -0
  686. package/src/helpers/bubbleHighlight.ts +47 -0
  687. package/src/helpers/checkUniqueUsers.tsx +15 -17
  688. package/src/helpers/constants/MODAL_COMPONENTS.tsx +4 -0
  689. package/src/helpers/constants/MODAL_TYPES.ts +3 -0
  690. package/src/helpers/createRoomFromApi.tsx +45 -4
  691. package/src/helpers/dedupeMembers.ts +29 -0
  692. package/src/helpers/ethoraLogger.ts +50 -0
  693. package/src/helpers/getUnnamedUsers.ts +7 -4
  694. package/src/helpers/historyPreloadConfig.ts +68 -0
  695. package/src/helpers/historyPreloadScheduler.ts +437 -199
  696. package/src/helpers/initRoomsPresence.ts +20 -7
  697. package/src/helpers/insertMessageWithDelimiter.ts +3 -0
  698. package/src/helpers/isLikelyMucJid.ts +72 -0
  699. package/src/helpers/isMessageSearchEnabled.ts +18 -0
  700. package/src/helpers/joiningRoom.ts +13 -0
  701. package/src/helpers/jumpThread.ts +102 -0
  702. package/src/helpers/jumpToMessage.ts +57 -0
  703. package/src/helpers/jumpWindow.ts +212 -0
  704. package/src/helpers/loadRoomHistory.ts +83 -0
  705. package/src/helpers/notificationPolicy.ts +82 -0
  706. package/src/helpers/openRoomAtMessage.ts +38 -0
  707. package/src/helpers/openThreadForJump.ts +96 -0
  708. package/src/helpers/parseMessageReference.ts +37 -8
  709. package/src/helpers/publicChatsEntry.ts +15 -0
  710. package/src/helpers/roomActivityScore.ts +45 -0
  711. package/src/helpers/roomUserCount.ts +44 -0
  712. package/src/helpers/runConfiguredHistoryPreload.ts +76 -0
  713. package/src/helpers/sessionAppId.ts +17 -0
  714. package/src/helpers/timestamp.ts +44 -0
  715. package/src/helpers/userResolver.ts +542 -0
  716. package/src/helpers/xmppIdShape.ts +46 -0
  717. package/src/hooks/useChatWrapperInit.ts +22 -4
  718. package/src/hooks/useHeapSender.tsx +54 -3
  719. package/src/hooks/useJumpToMessage.ts +538 -0
  720. package/src/hooks/useMessageLoaderQueue.tsx +40 -4
  721. package/src/hooks/useMessageQueue.ts +9 -5
  722. package/src/hooks/useRoomDirectory.ts +39 -0
  723. package/src/hooks/useRoomInitialization.tsx +33 -7
  724. package/src/hooks/useRoomMute.ts +42 -0
  725. package/src/hooks/useRoomState.tsx +6 -0
  726. package/src/hooks/useSendMessage.tsx +14 -2
  727. package/src/i18n/strings.ts +312 -0
  728. package/src/networking/api-requests/messageSearch.api.ts +130 -0
  729. package/src/networking/api-requests/publicChats.api.ts +71 -0
  730. package/src/networking/api-requests/roomMembers.api.ts +263 -11
  731. package/src/networking/api-requests/rooms.api.ts +52 -12
  732. package/src/networking/api-requests/xmppToken.api.ts +62 -0
  733. package/src/networking/ethoraEvents.ts +262 -0
  734. package/src/networking/stanzaHandlers.ts +145 -19
  735. package/src/networking/xmpp/allRoomPresences.xmpp.ts +155 -5
  736. package/src/networking/xmpp/getHistory.xmpp.ts +433 -47
  737. package/src/networking/xmpp/handleStanzas.xmpp.ts +8 -0
  738. package/src/networking/xmpp/historyCursor.ts +124 -0
  739. package/src/networking/xmpp/mamQueryIds.ts +11 -0
  740. package/src/networking/xmpp/mamRouter.ts +24 -10
  741. package/src/networking/xmpp/presenceInRoom.xmpp.ts +91 -68
  742. package/src/networking/xmpp/roomJoinSweep.ts +412 -0
  743. package/src/networking/xmpp/sendMediaMessage.xmpp.ts +0 -1
  744. package/src/networking/xmpp/sendTextMessage.xmpp.ts +0 -1
  745. package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +0 -1
  746. package/src/networking/xmppClient.ts +323 -47
  747. package/src/networking/xmppCredentials.ts +87 -0
  748. package/src/roomStore/Middleware/jumpThreadMiddleware.ts +163 -0
  749. package/src/roomStore/Middleware/reactionsMiddleware.tsx +4 -0
  750. package/src/roomStore/Middleware/unreadMidlleware.tsx +59 -11
  751. package/src/roomStore/chatSettingsSlice.ts +11 -0
  752. package/src/roomStore/index.ts +10 -3
  753. package/src/roomStore/persistence.ts +54 -0
  754. package/src/roomStore/roomsSlice.ts +547 -65
  755. package/src/roomStore/safeKey.ts +49 -0
  756. package/src/roomStore/usersSetCap.ts +37 -0
  757. package/src/types/models/media.model.ts +4 -0
  758. package/src/types/models/room.model.ts +18 -5
  759. package/src/types/models/xmpp.model.ts +15 -1
  760. package/src/types/types.ts +95 -5
  761. package/src/version.ts +1 -1
@@ -1,28 +1,31 @@
1
1
  "use strict";
2
2
 
3
- import React, { useEffect, useRef, useState } from 'react';
3
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { withFileToken } from '../../helpers/secureFileUrl';
5
- import { View, TouchableOpacity, Text, StyleSheet, ActivityIndicator, Platform } from 'react-native';
5
+ import { View, Pressable, Text, StyleSheet, ActivityIndicator, Platform } from 'react-native';
6
6
  import { createAudioPlayer, setAudioModeAsync } from 'expo-audio';
7
7
  import * as FileSystem from 'expo-file-system/legacy';
8
- import { WebView } from 'react-native-webview';
9
- import { PauseIcon, PlayIcon } from '../../assets/icons';
8
+ import Svg, { Path, Rect } from 'react-native-svg';
10
9
  import { useTheme } from '../../hooks/useTheme';
10
+ import { useT } from '../../i18n/useT';
11
11
  import { getIosAudioPlaybackCacheExtension } from '../../helpers/mimeToExtension';
12
+ import { MIN_BAR, formatClipTime, parseDurationSeconds, parseWaveForm, resamplePeaks } from '../../helpers/audioWaveform';
12
13
  import { pushLog as devPushLog } from '../../utils/devLogger';
14
+ import { MAX_DECODE_BYTES, decodeAudioClip } from './AudioDecoderHost';
13
15
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
- const formatTime = millis => {
15
- const safe = Number.isFinite(millis) && millis > 0 ? millis : 0;
16
- const totalSec = Math.floor(safe / 1000);
17
- const min = Math.floor(totalSec / 60);
18
- const sec = totalSec % 60;
19
- return `${min}:${sec.toString().padStart(2, '0')}`;
20
- };
21
- // Largest payload we will pull into JS for decoding (input opus) and the
22
- // largest WAV we will ship back over the bridge (output PCM). Voice
23
- // messages are a few dozen KB / a couple hundred KB of WAV; these only
24
- // guard against a pathological multi-minute attachment.
25
- const MAX_DECODE_BYTES = 25 * 1024 * 1024;
16
+ const SPEEDS = [1, 1.5, 2];
17
+ const BAR_WIDTH = 3;
18
+ const BAR_GAP = 2;
19
+ const WAVE_HEIGHT = 24;
20
+
21
+ /** Waveforms already worked out this session, by source URL. */
22
+ const analysisCache = new Map();
23
+
24
+ /**
25
+ * Only one voice note plays at a time, as in the messengers: starting one
26
+ * pauses whichever was playing.
27
+ */
28
+ let pauseActive = null;
26
29
  const hashString = value => {
27
30
  let hash = 0;
28
31
  for (let i = 0; i < value.length; i += 1) {
@@ -131,142 +134,93 @@ const getContainerExtension = container => {
131
134
  // Everything else (mp3/m4a/aac/wav), and ALL of Android (ExoPlayer handles
132
135
  // Opus natively), plays straight through expo-audio.
133
136
  const requiresWebAudioDecode = container => Platform.OS === 'ios' && (container === 'webm' || container === 'ogg');
137
+ const PlayGlyph = ({
138
+ color
139
+ }) => /*#__PURE__*/_jsx(Svg, {
140
+ width: 26,
141
+ height: 26,
142
+ viewBox: "0 0 24 24",
143
+ children: /*#__PURE__*/_jsx(Path, {
144
+ d: "M7.5 5.2v13.6c0 .95 1.05 1.5 1.8.95l9.9-6.8c.65-.45.65-1.45 0-1.9L9.3 4.25c-.75-.55-1.8 0-1.8.95z",
145
+ fill: color
146
+ })
147
+ });
148
+ const PauseGlyph = ({
149
+ color
150
+ }) => /*#__PURE__*/_jsxs(Svg, {
151
+ width: 24,
152
+ height: 24,
153
+ viewBox: "0 0 24 24",
154
+ children: [/*#__PURE__*/_jsx(Rect, {
155
+ x: 6.5,
156
+ y: 5,
157
+ width: 4,
158
+ height: 14,
159
+ rx: 1.4,
160
+ fill: color
161
+ }), /*#__PURE__*/_jsx(Rect, {
162
+ x: 13.5,
163
+ y: 5,
164
+ width: 4,
165
+ height: 14,
166
+ rx: 1.4,
167
+ fill: color
168
+ })]
169
+ });
134
170
 
135
- // The WebView is used ONLY as a decoder, never as a player. Two iOS facts
136
- // force this shape:
137
- // • `decodeAudioData` is the only iOS API that understands WebM/Ogg Opus,
138
- // and it works on a suspended AudioContext (decoding needs no gesture).
139
- // • Playing audio OUT of a WebView — via AudioContext OR an <audio>
140
- // element — is gated behind an in-page user gesture that an RN-side tap
141
- // cannot supply, so both hang silently.
142
- // Therefore the page decodes Opus → PCM, re-encodes to a WAV blob, and ships
143
- // the bytes back to RN as base64. RN writes the WAV to cache and plays it
144
- // through expo-audio (native, no gesture limits, real play/pause/seek).
145
- const WEB_DECODER_HTML = `<!doctype html>
146
- <html>
147
- <head>
148
- <meta charset="utf-8" />
149
- <meta name="viewport" content="width=device-width, initial-scale=1" />
150
- <style>html,body{margin:0;padding:0;width:1px;height:1px;overflow:hidden;background:transparent}</style>
151
- </head>
152
- <body>
153
- <script>
154
- (function () {
155
- var MAX_WAV = ${MAX_DECODE_BYTES};
156
- var post = function (payload) {
157
- if (window.ReactNativeWebView) {
158
- window.ReactNativeWebView.postMessage(JSON.stringify(payload));
159
- }
160
- };
161
- var ctx = null;
162
- var encodeWav = function (buffer) {
163
- var numCh = buffer.numberOfChannels;
164
- var frames = buffer.length;
165
- var sr = buffer.sampleRate;
166
- var bytesLen = 44 + frames * numCh * 2;
167
- var ab = new ArrayBuffer(bytesLen);
168
- var view = new DataView(ab);
169
- var off = 0;
170
- var ws = function (s) { for (var i = 0; i < s.length; i += 1) { view.setUint8(off++, s.charCodeAt(i)); } };
171
- var u32 = function (d) { view.setUint32(off, d, true); off += 4; };
172
- var u16 = function (d) { view.setUint16(off, d, true); off += 2; };
173
- ws('RIFF'); u32(bytesLen - 8); ws('WAVE');
174
- ws('fmt '); u32(16); u16(1); u16(numCh); u32(sr); u32(sr * numCh * 2); u16(numCh * 2); u16(16);
175
- ws('data'); u32(frames * numCh * 2);
176
- var chans = [];
177
- for (var c = 0; c < numCh; c += 1) { chans.push(buffer.getChannelData(c)); }
178
- for (var f = 0; f < frames; f += 1) {
179
- for (var c2 = 0; c2 < numCh; c2 += 1) {
180
- var s = Math.max(-1, Math.min(1, chans[c2][f]));
181
- view.setInt16(off, s < 0 ? s * 0x8000 : s * 0x7fff, true);
182
- off += 2;
183
- }
184
- }
185
- return ab;
186
- };
187
- var abToBase64 = function (ab) {
188
- var bytes = new Uint8Array(ab);
189
- var binary = '';
190
- var chunk = 0x8000;
191
- for (var i = 0; i < bytes.length; i += chunk) {
192
- binary += String.fromCharCode.apply(null, bytes.subarray(i, i + chunk));
193
- }
194
- return window.btoa(binary);
195
- };
196
- window.__decodeAudio = function (base64) {
197
- try {
198
- if (!ctx) {
199
- var Ctx = window.AudioContext || window.webkitAudioContext;
200
- ctx = new Ctx();
201
- }
202
- var bin = window.atob(base64);
203
- var len = bin.length;
204
- var bytes = new Uint8Array(len);
205
- for (var i = 0; i < len; i += 1) { bytes[i] = bin.charCodeAt(i); }
206
- ctx.decodeAudioData(
207
- bytes.buffer,
208
- function (decoded) {
209
- try {
210
- var wav = encodeWav(decoded);
211
- if (wav.byteLength > MAX_WAV) {
212
- post({ type: 'error', message: 'audio_too_long' });
213
- return;
214
- }
215
- post({
216
- type: 'decoded',
217
- durationMillis: Math.floor(decoded.duration * 1000),
218
- wavBase64: abToBase64(wav),
219
- });
220
- } catch (e) {
221
- post({ type: 'error', message: 'encode_failed:' + (e && e.message ? e.message : e) });
222
- }
223
- },
224
- function (err) {
225
- post({ type: 'error', message: 'decode_failed:' + (err && err.message ? err.message : err) });
226
- }
227
- );
228
- } catch (e) {
229
- post({ type: 'error', message: 'load_failed:' + (e && e.message ? e.message : e) });
230
- }
231
- };
232
- post({ type: 'bridge_ready' });
233
- })();
234
- </script>
235
- </body>
236
- </html>`;
171
+ /**
172
+ * A voice message the way the messengers draw it: a round play button,
173
+ * the clip's waveform filling in as it plays (tap it to jump), and its
174
+ * length — elapsed while playing — under the bars.
175
+ *
176
+ * The bars come from the sender's `waveForm` when there is one, otherwise
177
+ * from decoding the file in the shared decoder (AudioDecoderHost), which
178
+ * is also what makes the web client's WebM/Opus playable on iOS.
179
+ */
237
180
  const AudioMessage = ({
238
181
  src,
239
182
  mimeType,
240
183
  fileName,
241
- originalName
184
+ originalName,
185
+ duration: durationProp,
186
+ waveForm,
187
+ isUser = false
242
188
  }) => {
243
189
  const theme = useTheme();
190
+ const t = useT();
244
191
  const soundRef = useRef(null);
245
- // expo-audio delivers progress through an event subscription instead of
246
- // expo-av's `onPlaybackStatusUpdate` callback argument, so the handle has
247
- // to be held and removed alongside the player itself.
192
+ // expo-audio delivers progress through an event subscription, so the
193
+ // handle has to be held and removed alongside the player itself.
248
194
  const statusSubRef = useRef(null);
249
- const webViewRef = useRef(null);
250
195
  const preparedRef = useRef(null);
251
- const bridgeReadyRef = useRef(false);
252
- const injectedRef = useRef(false);
196
+ const preparingRef = useRef(null);
253
197
  const didFinishRef = useRef(false);
254
198
  const unmountedRef = useRef(false);
255
199
  const loadingTimerRef = useRef(null);
200
+ // Where to start once the player is up (a tap on the bars before play).
201
+ const pendingSeekRef = useRef(null);
202
+ const rateRef = useRef(1);
203
+
204
+ // The file's identity without its (rotating) access token, so caches
205
+ // survive a token refresh.
206
+ const cacheKey = src.split(/[?#]/)[0] || src;
207
+ const metaPeaks = useMemo(() => parseWaveForm(waveForm), [waveForm]);
208
+ const metaDuration = parseDurationSeconds(durationProp) * 1000;
256
209
  const [isPlaying, setIsPlaying] = useState(false);
257
210
  const [isLoading, setIsLoading] = useState(false);
258
211
  const [playbackError, setPlaybackError] = useState(false);
259
212
  const [position, setPosition] = useState(0);
260
- const [duration, setDuration] = useState(0);
261
- const [webViewEnabled, setWebViewEnabled] = useState(false);
262
- const primaryColor = theme.primary;
263
- const onPrimary = theme.textOnPrimary;
264
- const trackStyle = {
265
- backgroundColor: theme.border
266
- };
267
- const timeStyle = {
268
- color: theme.textSecondary
269
- };
213
+ const [duration, setDuration] = useState(metaDuration);
214
+ const [peaks, setPeaks] = useState(metaPeaks);
215
+ const [rate, setRate] = useState(1);
216
+ const [waveWidth, setWaveWidth] = useState(0);
217
+
218
+ // The brand colour from the config (the same tint as the composer's mic
219
+ // and send buttons), on own and others' bubbles alike.
220
+ const ink = theme.icon;
221
+ const buttonFill = ink;
222
+ const buttonGlyph = theme.textOnPrimary;
223
+ const metaColor = isUser ? theme.messageTextUser : theme.textSecondary;
270
224
  const clearLoadingGuard = () => {
271
225
  if (loadingTimerRef.current) {
272
226
  clearTimeout(loadingTimerRef.current);
@@ -276,8 +230,7 @@ const AudioMessage = ({
276
230
 
277
231
  // expo-audio players are native shared objects: they are NOT garbage
278
232
  // collected with the component, so every one we create has to be
279
- // explicitly `remove()`d (expo-av's `unloadAsync` equivalent) and its
280
- // status subscription torn down first.
233
+ // explicitly `remove()`d and its status subscription torn down first.
281
234
  const releasePlayer = () => {
282
235
  statusSubRef.current?.remove();
283
236
  statusSubRef.current = null;
@@ -296,6 +249,25 @@ const AudioMessage = ({
296
249
  }
297
250
  }
298
251
  };
252
+
253
+ // Stable for this bubble's lifetime: what another bubble calls to pause
254
+ // this one when it starts playing.
255
+ const pauseSelf = useRef(() => {
256
+ try {
257
+ soundRef.current?.pause();
258
+ } catch {
259
+ /* released */
260
+ }
261
+ if (!unmountedRef.current) {
262
+ setIsPlaying(false);
263
+ }
264
+ }).current;
265
+ const claimPlayback = () => {
266
+ if (pauseActive && pauseActive !== pauseSelf) {
267
+ pauseActive();
268
+ }
269
+ pauseActive = pauseSelf;
270
+ };
299
271
  const fail = reason => {
300
272
  clearLoadingGuard();
301
273
  if (unmountedRef.current) {
@@ -318,74 +290,76 @@ const AudioMessage = ({
318
290
  };
319
291
 
320
292
  // Hard ceiling on the loading spinner — whatever stalls (a hung network
321
- // request, a WebView that never decodes, a player that never loads)
322
- // the control resolves to an error state instead of spinning forever
323
- // (the customer-reported "stuck spinner").
293
+ // request, a decode that never returns, a player that never loads) the
294
+ // control resolves to an error state instead of spinning forever.
324
295
  const armLoadingGuard = () => {
325
296
  clearLoadingGuard();
326
297
  loadingTimerRef.current = setTimeout(() => {
327
298
  if (!unmountedRef.current && !soundRef.current) {
328
299
  fail(new Error('audio_timeout'));
329
300
  }
330
- }, 15000);
301
+ }, 25000);
331
302
  };
332
- const prepareSource = async () => {
333
- if (preparedRef.current) {
334
- return preparedRef.current;
303
+ const cacheBase = () => FileSystem.cacheDirectory ? `${FileSystem.cacheDirectory}ethora-audio-${hashString(cacheKey)}` : null;
304
+ const sniff = async uri => {
305
+ try {
306
+ const head = await FileSystem.readAsStringAsync(uri, {
307
+ encoding: FileSystem.EncodingType.Base64,
308
+ position: 0,
309
+ length: 32
310
+ });
311
+ return detectContainer(decodeBase64Head(head, 16));
312
+ } catch {
313
+ return 'unknown';
335
314
  }
336
- if (!/^https?:\/\//i.test(src)) {
337
- const prepared = {
338
- localUri: src,
339
- container: 'unknown'
340
- };
341
- preparedRef.current = prepared;
342
- return prepared;
315
+ };
316
+
317
+ // Download once (shared by the waveform and playback), sniff the real
318
+ // container, and give the cached file a real extension so AVFoundation /
319
+ // ExoPlayer pick the right demuxer instead of choking on an opaque
320
+ // `.blob` (the original `audio_timeout` cause).
321
+ const prepareSource = () => {
322
+ if (preparedRef.current) {
323
+ return Promise.resolve(preparedRef.current);
343
324
  }
344
- const cacheDirectory = FileSystem.cacheDirectory;
345
- if (!cacheDirectory) {
346
- const prepared = {
347
- localUri: src,
348
- container: 'unknown'
349
- };
350
- preparedRef.current = prepared;
351
- return prepared;
325
+ if (preparingRef.current) {
326
+ return preparingRef.current;
352
327
  }
353
- const base = `${cacheDirectory}ethora-audio-${hashString(src)}`;
354
- const part = `${base}.part`;
355
- const partInfo = await FileSystem.getInfoAsync(part);
356
- if (!partInfo.exists) {
357
- const download = await withTimeout(FileSystem.downloadAsync(withFileToken(src), part), 12000, 'audio_download_timeout');
358
- logAudioDebug('audio downloaded', {
359
- src,
360
- uri: download.status === 200 ? download.uri : undefined,
361
- status: download.status
362
- });
363
- if (download.status !== 200) {
364
- throw new Error(`audio_download_status_${download.status}`);
328
+ const run = async () => {
329
+ const base = cacheBase();
330
+ if (!/^https?:\/\//i.test(src) || !base) {
331
+ // Local file (the sender's own clip while it uploads).
332
+ return {
333
+ localUri: src,
334
+ container: await sniff(src)
335
+ };
365
336
  }
366
- }
367
- const head = await FileSystem.readAsStringAsync(part, {
368
- encoding: FileSystem.EncodingType.Base64,
369
- position: 0,
370
- length: 32
371
- });
372
- const container = detectContainer(decodeBase64Head(head, 16));
373
- let base64;
374
- let localUri = part;
375
- if (requiresWebAudioDecode(container)) {
376
- // WebView-decode path: extension is irrelevant, we hand over the bytes.
377
- const info = await FileSystem.getInfoAsync(part);
378
- const size = 'size' in info ? info.size ?? 0 : 0;
379
- if (size > MAX_DECODE_BYTES) {
380
- throw new Error('audio_too_large_to_decode');
337
+ const wav = `${base}.wav`;
338
+ if ((await FileSystem.getInfoAsync(wav)).exists) {
339
+ return {
340
+ localUri: wav,
341
+ container: 'wav',
342
+ playable: true
343
+ };
344
+ }
345
+ const part = `${base}.part`;
346
+ if (!(await FileSystem.getInfoAsync(part)).exists) {
347
+ const download = await withTimeout(FileSystem.downloadAsync(withFileToken(src), part), 12000, 'audio_download_timeout');
348
+ if (download.status !== 200) {
349
+ await FileSystem.deleteAsync(part, {
350
+ idempotent: true
351
+ }).catch(() => {});
352
+ throw new Error(`audio_download_status_${download.status}`);
353
+ }
354
+ }
355
+ const container = await sniff(part);
356
+ if (requiresWebAudioDecode(container)) {
357
+ // Decoded to WAV before playing; the extension is irrelevant.
358
+ return {
359
+ localUri: part,
360
+ container
361
+ };
381
362
  }
382
- base64 = await FileSystem.readAsStringAsync(part, {
383
- encoding: FileSystem.EncodingType.Base64
384
- });
385
- } else {
386
- // Native path: give the cached file a real extension so AVFoundation
387
- // / ExoPlayer pick the right demuxer instead of choking on an opaque
388
- // `.blob` (the original `audio_timeout` cause).
389
363
  const ext = container !== 'unknown' ? getContainerExtension(container) : getIosAudioPlaybackCacheExtension({
390
364
  mime: mimeType,
391
365
  fileName,
@@ -393,8 +367,7 @@ const AudioMessage = ({
393
367
  url: src
394
368
  });
395
369
  const finalUri = `${base}${ext}`;
396
- const finalInfo = await FileSystem.getInfoAsync(finalUri);
397
- if (finalInfo.exists) {
370
+ if ((await FileSystem.getInfoAsync(finalUri)).exists) {
398
371
  await FileSystem.deleteAsync(part, {
399
372
  idempotent: true
400
373
  }).catch(() => {});
@@ -404,42 +377,171 @@ const AudioMessage = ({
404
377
  to: finalUri
405
378
  });
406
379
  }
407
- localUri = finalUri;
408
- }
409
- const prepared = {
410
- localUri,
411
- container,
412
- base64
380
+ return {
381
+ localUri: finalUri,
382
+ container
383
+ };
413
384
  };
414
- preparedRef.current = prepared;
415
- logAudioDebug('audio prepared', {
416
- src,
417
- mimeType,
418
- fileName,
419
- originalName,
420
- container,
421
- platform: Platform.OS,
422
- decode: requiresWebAudioDecode(container) ? 'web-audio' : 'native'
385
+ const pending = run().then(prepared => {
386
+ preparedRef.current = prepared;
387
+ preparingRef.current = null;
388
+ logAudioDebug('audio prepared', {
389
+ src,
390
+ container: prepared.container,
391
+ platform: Platform.OS
392
+ });
393
+ return prepared;
394
+ }, error => {
395
+ preparingRef.current = null;
396
+ throw error;
397
+ });
398
+ preparingRef.current = pending;
399
+ return pending;
400
+ };
401
+ const readBase64 = async uri => {
402
+ const info = await FileSystem.getInfoAsync(uri);
403
+ const size = 'size' in info ? info.size ?? 0 : 0;
404
+ if (size > MAX_DECODE_BYTES) {
405
+ throw new Error('audio_too_large_to_decode');
406
+ }
407
+ return FileSystem.readAsStringAsync(uri, {
408
+ encoding: FileSystem.EncodingType.Base64
423
409
  });
424
- return prepared;
425
410
  };
426
- const maybeInjectDecode = () => {
427
- const base64 = preparedRef.current?.base64;
428
- if (bridgeReadyRef.current && base64 && !injectedRef.current && webViewRef.current) {
429
- injectedRef.current = true;
430
- webViewRef.current.injectJavaScript(`window.__decodeAudio(${JSON.stringify(base64)}); true;`);
411
+ const remember = analysis => {
412
+ analysisCache.set(cacheKey, analysis);
413
+ const base = cacheBase();
414
+ if (base) {
415
+ FileSystem.writeAsStringAsync(`${base}.wave.json`, JSON.stringify(analysis)).catch(() => {});
416
+ }
417
+ };
418
+ const applyAnalysis = analysis => {
419
+ if (unmountedRef.current) {
420
+ return;
421
+ }
422
+ if (!metaPeaks && analysis.peaks.length) {
423
+ setPeaks(analysis.peaks);
424
+ }
425
+ if (analysis.durationMillis > 0) {
426
+ setDuration(current => current || analysis.durationMillis);
427
+ }
428
+ };
429
+
430
+ // Decode the clip once: its bars, its real length (MediaRecorder writes
431
+ // WebM without a duration header), and — where the native player cannot
432
+ // read it — a WAV copy to play.
433
+ const decodeClip = async prepared => {
434
+ const wantWav = requiresWebAudioDecode(prepared.container) && !prepared.playable;
435
+ const clip = await decodeAudioClip(await readBase64(prepared.localUri), {
436
+ wantWav
437
+ });
438
+ const analysis = {
439
+ peaks: clip.peaks,
440
+ durationMillis: clip.durationMillis
441
+ };
442
+ remember(analysis);
443
+ applyAnalysis(analysis);
444
+ const base = cacheBase();
445
+ if (clip.wavBase64 && base) {
446
+ const wavUri = `${base}.wav`;
447
+ await FileSystem.writeAsStringAsync(wavUri, clip.wavBase64, {
448
+ encoding: FileSystem.EncodingType.Base64
449
+ });
450
+ await FileSystem.deleteAsync(prepared.localUri, {
451
+ idempotent: true
452
+ }).catch(() => {});
453
+ preparedRef.current = {
454
+ localUri: wavUri,
455
+ container: 'wav',
456
+ playable: true
457
+ };
431
458
  }
459
+ return preparedRef.current ?? prepared;
432
460
  };
433
461
 
434
- // expo-audio reports times in SECONDS (expo-av used milliseconds); the
435
- // whole UI below is millisecond-based, so convert at this boundary and
436
- // nowhere else.
462
+ // Full reset whenever the source changes (component reused for a new
463
+ // message / re-render with a different src).
464
+ const lastSrcRef = useRef(src);
465
+ useEffect(() => {
466
+ if (lastSrcRef.current === src) {
467
+ return;
468
+ }
469
+ lastSrcRef.current = src;
470
+ releasePlayer();
471
+ preparedRef.current = null;
472
+ preparingRef.current = null;
473
+ didFinishRef.current = false;
474
+ pendingSeekRef.current = null;
475
+ clearLoadingGuard();
476
+ setIsPlaying(false);
477
+ setIsLoading(false);
478
+ setPlaybackError(false);
479
+ setPosition(0);
480
+ setDuration(metaDuration);
481
+ setPeaks(metaPeaks);
482
+ // eslint-disable-next-line react-hooks/exhaustive-deps
483
+ }, [src]);
484
+
485
+ // The waveform, as soon as the bubble shows: from the message itself,
486
+ // from this session's cache, from the disk cache, or by decoding.
487
+ useEffect(() => {
488
+ if (!src || metaPeaks && metaDuration > 0) {
489
+ return;
490
+ }
491
+ const cached = analysisCache.get(cacheKey);
492
+ if (cached) {
493
+ applyAnalysis(cached);
494
+ return;
495
+ }
496
+ let cancelled = false;
497
+ (async () => {
498
+ const base = cacheBase();
499
+ if (base) {
500
+ try {
501
+ const stored = await FileSystem.readAsStringAsync(`${base}.wave.json`);
502
+ const parsed = JSON.parse(stored);
503
+ if (Array.isArray(parsed?.peaks)) {
504
+ analysisCache.set(cacheKey, parsed);
505
+ if (!cancelled) {
506
+ applyAnalysis(parsed);
507
+ }
508
+ return;
509
+ }
510
+ } catch {
511
+ /* not analysed yet */
512
+ }
513
+ }
514
+ try {
515
+ const prepared = await prepareSource();
516
+ if (cancelled) {
517
+ return;
518
+ }
519
+ await decodeClip(prepared);
520
+ } catch (error) {
521
+ // Not fatal: the bubble keeps flat bars and still plays.
522
+ logAudioDebug('audio waveform unavailable', {
523
+ src,
524
+ error: error instanceof Error ? error.message : String(error)
525
+ });
526
+ }
527
+ })();
528
+ return () => {
529
+ cancelled = true;
530
+ };
531
+ // eslint-disable-next-line react-hooks/exhaustive-deps
532
+ }, [src]);
533
+
534
+ // expo-audio reports times in SECONDS; the UI is millisecond-based, so
535
+ // convert at this boundary and nowhere else.
437
536
  const onPlaybackStatusUpdate = status => {
438
- if (!status.isLoaded) {
537
+ if (!status.isLoaded || unmountedRef.current) {
439
538
  return;
440
539
  }
441
540
  setPosition(Math.max(0, status.currentTime * 1000));
442
- setDuration(status.duration > 0 ? status.duration * 1000 : 0);
541
+ // A WebM without a duration header reports 0 here: keep the decoded one.
542
+ if (status.duration > 0) {
543
+ setDuration(status.duration * 1000);
544
+ }
443
545
  setIsPlaying(status.playing);
444
546
  if (status.didJustFinish) {
445
547
  didFinishRef.current = true;
@@ -460,15 +562,13 @@ const AudioMessage = ({
460
562
  } catch {
461
563
  /* non-fatal — still plays through the ringer channel */
462
564
  }
463
- // expo-audio has no awaitable `createAsync`: the player is constructed
464
- // synchronously and loads in the background, reporting `isLoaded` via
465
- // the status event. Wait for that first loaded status (same 12s budget
466
- // expo-av's createAsync had) so the spinner still resolves to either
467
- // real playback or the error state, never to a silent dead button.
565
+ // expo-audio has no awaitable create: the player loads in the
566
+ // background and reports `isLoaded` via the status event. Wait for it
567
+ // so the spinner resolves to real playback or the error state.
468
568
  const player = createAudioPlayer({
469
569
  uri: localUri
470
570
  }, {
471
- updateInterval: 250
571
+ updateInterval: 100
472
572
  });
473
573
  let settleLoaded = null;
474
574
  const loaded = new Promise(resolve => {
@@ -495,81 +595,33 @@ const AudioMessage = ({
495
595
  }
496
596
  soundRef.current = player;
497
597
  statusSubRef.current = subscription;
598
+ if (rateRef.current !== 1) {
599
+ player.shouldCorrectPitch = true;
600
+ player.setPlaybackRate(rateRef.current);
601
+ }
602
+ const seek = pendingSeekRef.current;
603
+ pendingSeekRef.current = null;
604
+ if (seek !== null && player.duration > 0) {
605
+ await player.seekTo(seek * player.duration);
606
+ }
607
+ claimPlayback();
498
608
  player.play();
499
609
  clearLoadingGuard();
500
610
  setIsLoading(false);
501
611
  setPlaybackError(false);
502
612
  setIsPlaying(true);
503
- logAudioDebug('native audio playback started', {
504
- src,
505
- container: preparedRef.current?.container,
506
- localUri
507
- });
508
- };
509
-
510
- // WebView finished decoding Opus → WAV: persist the WAV and play it
511
- // natively. From here it is indistinguishable from any other native clip.
512
- const onDecoded = async (durationMillis, wavBase64) => {
513
- try {
514
- const cacheDirectory = FileSystem.cacheDirectory || '';
515
- const wavUri = `${cacheDirectory}ethora-audio-${hashString(src)}.wav`;
516
- await FileSystem.writeAsStringAsync(wavUri, wavBase64, {
517
- encoding: FileSystem.EncodingType.Base64
518
- });
519
- if (unmountedRef.current) {
520
- return;
521
- }
522
- preparedRef.current = {
523
- localUri: wavUri,
524
- container: preparedRef.current?.container ?? 'webm'
525
- };
526
- if (durationMillis > 0) {
527
- setDuration(durationMillis);
528
- }
529
- setWebViewEnabled(false); // decoder no longer needed
530
- await startNativePlayback(wavUri);
531
- } catch (error) {
532
- fail(error);
533
- }
534
- };
535
- const handleWebMessage = event => {
536
- let payload;
537
- try {
538
- payload = JSON.parse(event.nativeEvent.data);
539
- } catch {
540
- return;
541
- }
542
- if (payload.type === 'bridge_ready') {
543
- bridgeReadyRef.current = true;
544
- maybeInjectDecode();
545
- return;
546
- }
547
- if (payload.type === 'decoded') {
548
- logAudioDebug('web audio decoded', {
549
- src,
550
- durationMillis: payload.durationMillis,
551
- wavBytes: payload.wavBase64 ? payload.wavBase64.length : 0
552
- });
553
- void onDecoded(payload.durationMillis, payload.wavBase64);
554
- return;
555
- }
556
- // type === 'error'
557
- fail(new Error(payload.message || 'web_audio_error'));
558
613
  };
559
614
  const togglePlayback = async () => {
560
615
  if (!src) {
561
616
  return;
562
617
  }
563
618
 
564
- // Already-loaded native sound (covers both native formats AND the
565
- // decoded-WAV from the WebView path) → plain toggle.
619
+ // Already-loaded player → plain toggle.
566
620
  if (soundRef.current) {
567
621
  try {
568
- // `play()` / `pause()` emit NO status event of their own (expo-av's
569
- // playAsync/pauseAsync resolved with one). Playing state only ever
570
- // reaches us through the periodic time observer, and that observer
571
- // stops while paused — so without setting this here the button
572
- // would latch on "pause" forever and never resume.
622
+ // `play()` / `pause()` emit no status event of their own, and the
623
+ // time observer stops while paused — so set the state here or the
624
+ // button latches on "pause" forever.
573
625
  if (isPlaying) {
574
626
  soundRef.current.pause();
575
627
  setIsPlaying(false);
@@ -578,6 +630,7 @@ const AudioMessage = ({
578
630
  await soundRef.current.seekTo(0);
579
631
  didFinishRef.current = false;
580
632
  }
633
+ claimPlayback();
581
634
  soundRef.current.play();
582
635
  setIsPlaying(true);
583
636
  }
@@ -587,22 +640,17 @@ const AudioMessage = ({
587
640
  return;
588
641
  }
589
642
 
590
- // First tap → prepare (download + sniff) then route to the right engine.
643
+ // First tap → download (unless the waveform already did), decode where
644
+ // the native player cannot, then play.
591
645
  setPlaybackError(false);
592
646
  setIsLoading(true);
593
647
  armLoadingGuard();
594
648
  try {
595
- const prepared = await withTimeout(prepareSource(), 14000, 'audio_prepare_timeout');
596
- if (unmountedRef.current) {
597
- return;
649
+ let prepared = await withTimeout(prepareSource(), 14000, 'audio_prepare_timeout');
650
+ if (requiresWebAudioDecode(prepared.container) && !prepared.playable) {
651
+ prepared = await decodeClip(prepared);
598
652
  }
599
- if (requiresWebAudioDecode(prepared.container)) {
600
- // Mount the decoder WebView; decoding + playback continue when the
601
- // 'decoded' message arrives (→ onDecoded → startNativePlayback).
602
- if (!webViewEnabled) {
603
- setWebViewEnabled(true);
604
- }
605
- maybeInjectDecode();
653
+ if (unmountedRef.current) {
606
654
  return;
607
655
  }
608
656
  await startNativePlayback(prepared.localUri);
@@ -610,166 +658,191 @@ const AudioMessage = ({
610
658
  fail(error);
611
659
  }
612
660
  };
661
+
662
+ // A tap on the bars jumps there (and starts playing, as in Telegram).
663
+ const seekFromTap = event => {
664
+ if (!waveWidth || playbackError) {
665
+ return;
666
+ }
667
+ const ratio = Math.max(0, Math.min(1, event.nativeEvent.locationX / waveWidth));
668
+ const player = soundRef.current;
669
+ if (player && player.duration > 0) {
670
+ didFinishRef.current = false;
671
+ void player.seekTo(ratio * player.duration);
672
+ setPosition(ratio * player.duration * 1000);
673
+ if (!isPlaying) {
674
+ claimPlayback();
675
+ player.play();
676
+ setIsPlaying(true);
677
+ }
678
+ return;
679
+ }
680
+ if (isLoading) {
681
+ return;
682
+ }
683
+ pendingSeekRef.current = ratio;
684
+ void togglePlayback();
685
+ };
686
+ const cycleSpeed = () => {
687
+ const next = SPEEDS[(SPEEDS.indexOf(rate) + 1) % SPEEDS.length];
688
+ rateRef.current = next;
689
+ setRate(next);
690
+ const player = soundRef.current;
691
+ if (player) {
692
+ player.shouldCorrectPitch = true;
693
+ player.setPlaybackRate(next);
694
+ }
695
+ };
613
696
  useEffect(() => {
614
697
  unmountedRef.current = false;
615
698
  return () => {
616
699
  unmountedRef.current = true;
700
+ if (pauseActive === pauseSelf) {
701
+ pauseActive = null;
702
+ }
617
703
  clearLoadingGuard();
618
704
  releasePlayer();
619
705
  };
620
706
  // eslint-disable-next-line react-hooks/exhaustive-deps
621
707
  }, []);
622
-
623
- // Full reset whenever the source changes (component reused for a new
624
- // message / re-render with a different src).
625
- useEffect(() => {
626
- releasePlayer();
627
- preparedRef.current = null;
628
- bridgeReadyRef.current = false;
629
- injectedRef.current = false;
630
- didFinishRef.current = false;
631
- clearLoadingGuard();
632
- setIsPlaying(false);
633
- setIsLoading(false);
634
- setPlaybackError(false);
635
- setPosition(0);
636
- setDuration(0);
637
- setWebViewEnabled(false);
638
- // eslint-disable-next-line react-hooks/exhaustive-deps
639
- }, [src]);
708
+ const onWaveLayout = event => {
709
+ const width = Math.floor(event.nativeEvent.layout.width);
710
+ if (width !== waveWidth) {
711
+ setWaveWidth(width);
712
+ }
713
+ };
714
+ const barCount = waveWidth ? Math.max(8, Math.floor((waveWidth + BAR_GAP) / (BAR_WIDTH + BAR_GAP))) : 0;
715
+ const bars = useMemo(() => barCount ? resamplePeaks(peaks ?? new Array(barCount).fill(MIN_BAR), barCount) : [], [peaks, barCount]);
640
716
  const progress = duration > 0 ? Math.min(1, position / duration) : 0;
717
+ const playedBars = Math.round(progress * bars.length);
718
+ const started = isPlaying || position > 0;
719
+ const timeLabel = playbackError ? t('media.audioUnavailable') : started ? formatClipTime(position) : duration > 0 ? formatClipTime(duration) : '';
641
720
  return /*#__PURE__*/_jsxs(View, {
642
- style: styles.root,
643
- children: [webViewEnabled ? /*#__PURE__*/_jsx(View, {
644
- style: styles.decoderHost,
645
- pointerEvents: "none",
646
- children: /*#__PURE__*/_jsx(WebView, {
647
- ref: webViewRef,
648
- source: {
649
- html: WEB_DECODER_HTML
650
- },
651
- originWhitelist: ['*'],
652
- onMessage: handleWebMessage,
653
- javaScriptEnabled: true,
654
- domStorageEnabled: false,
655
- scrollEnabled: false,
656
- pointerEvents: "none",
657
- showsHorizontalScrollIndicator: false,
658
- showsVerticalScrollIndicator: false,
659
- style: styles.hiddenWebView
721
+ style: styles.container,
722
+ testID: "audio-message",
723
+ children: [/*#__PURE__*/_jsx(Pressable, {
724
+ testID: "audio-play-button",
725
+ accessibilityRole: "button",
726
+ accessibilityLabel: t(isPlaying ? 'media.audioPause' : 'media.audioPlay'),
727
+ style: ({
728
+ pressed
729
+ }) => [styles.playButton, {
730
+ backgroundColor: buttonFill,
731
+ opacity: pressed ? 0.85 : 1
732
+ }],
733
+ onPress: togglePlayback,
734
+ disabled: isLoading,
735
+ hitSlop: 6,
736
+ children: isLoading ? /*#__PURE__*/_jsx(ActivityIndicator, {
737
+ size: "small",
738
+ color: buttonGlyph
739
+ }) : isPlaying ? /*#__PURE__*/_jsx(PauseGlyph, {
740
+ color: buttonGlyph
741
+ }) : /*#__PURE__*/_jsx(PlayGlyph, {
742
+ color: buttonGlyph
660
743
  })
661
- }) : null, /*#__PURE__*/_jsxs(View, {
662
- style: styles.container,
663
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
664
- testID: "audio-play-button",
665
- accessibilityLabel: "audio-play-button",
666
- style: [styles.playButton, {
667
- backgroundColor: primaryColor
668
- }],
669
- onPress: togglePlayback,
670
- disabled: isLoading,
671
- activeOpacity: 0.85,
672
- children: isLoading ? /*#__PURE__*/_jsx(ActivityIndicator, {
673
- size: "small",
674
- color: onPrimary
675
- }) : /*#__PURE__*/_jsx(View, {
676
- style: styles.iconWrap,
677
- children: isPlaying ? /*#__PURE__*/_jsx(PauseIcon, {
678
- width: 18,
679
- height: 18,
680
- color: onPrimary
681
- }) : /*#__PURE__*/_jsx(PlayIcon, {
682
- width: 18,
683
- height: 18,
684
- color: onPrimary
685
- })
686
- })
744
+ }), /*#__PURE__*/_jsxs(View, {
745
+ style: styles.track,
746
+ children: [/*#__PURE__*/_jsx(Pressable, {
747
+ testID: "audio-waveform",
748
+ accessibilityElementsHidden: true,
749
+ importantForAccessibility: "no-hide-descendants",
750
+ onPress: seekFromTap,
751
+ onLayout: onWaveLayout,
752
+ style: styles.wave,
753
+ children: bars.map((value, index) => /*#__PURE__*/_jsx(View, {
754
+ style: [styles.bar, {
755
+ height: Math.max(BAR_WIDTH, Math.round(value * WAVE_HEIGHT)),
756
+ backgroundColor: ink,
757
+ opacity: index < playedBars ? 1 : 0.35
758
+ }]
759
+ }, index))
687
760
  }), /*#__PURE__*/_jsxs(View, {
688
- style: styles.progressContainer,
689
- children: [/*#__PURE__*/_jsx(View, {
690
- style: [styles.progressTrack, trackStyle],
691
- children: /*#__PURE__*/_jsx(View, {
692
- style: [styles.progressFill, {
693
- width: `${progress * 100}%`,
694
- backgroundColor: primaryColor
695
- }]
761
+ style: styles.metaRow,
762
+ children: [/*#__PURE__*/_jsx(Text, {
763
+ style: [styles.time, {
764
+ color: metaColor
765
+ }],
766
+ numberOfLines: 1,
767
+ children: timeLabel
768
+ }), started && !playbackError ? /*#__PURE__*/_jsx(Pressable, {
769
+ testID: "audio-speed",
770
+ accessibilityRole: "button",
771
+ accessibilityLabel: t('media.audioSpeed'),
772
+ onPress: cycleSpeed,
773
+ hitSlop: 8,
774
+ style: [styles.speed, {
775
+ backgroundColor: ink
776
+ }],
777
+ children: /*#__PURE__*/_jsx(Text, {
778
+ style: [styles.speedText, {
779
+ color: buttonGlyph
780
+ }],
781
+ children: `${rate}×`
696
782
  })
697
- }), /*#__PURE__*/_jsxs(View, {
698
- style: styles.progressRow,
699
- children: [/*#__PURE__*/_jsx(Text, {
700
- style: [styles.time, timeStyle],
701
- children: playbackError ? 'Audio unavailable' : formatTime(position)
702
- }), /*#__PURE__*/_jsx(Text, {
703
- style: [styles.time, timeStyle],
704
- children: !playbackError && duration > 0 ? formatTime(duration) : ''
705
- })]
706
- })]
783
+ }) : null]
707
784
  })]
708
785
  })]
709
786
  });
710
787
  };
711
788
  export default AudioMessage;
712
789
  const styles = StyleSheet.create({
713
- root: {
714
- position: 'relative'
715
- },
716
- decoderHost: {
717
- position: 'absolute',
718
- top: 0,
719
- left: 0,
720
- width: 0,
721
- height: 0,
722
- overflow: 'hidden',
723
- opacity: 0
724
- },
725
790
  container: {
726
791
  flexDirection: 'row',
727
792
  alignItems: 'center',
728
- gap: 12,
729
- paddingHorizontal: 12,
730
- paddingVertical: 10,
731
- width: 260,
793
+ gap: 10,
794
+ // Media bubbles carry no padding of their own (images run edge to
795
+ // edge), so the player brings the text bubble's inset with it.
796
+ paddingTop: 10,
797
+ paddingLeft: 10,
798
+ paddingRight: 12,
799
+ width: 250,
732
800
  maxWidth: '100%'
733
801
  },
734
802
  playButton: {
735
- width: 44,
736
- height: 44,
737
- borderRadius: 22,
803
+ width: 46,
804
+ height: 46,
805
+ borderRadius: 23,
738
806
  alignItems: 'center',
739
807
  justifyContent: 'center'
740
808
  },
741
- iconWrap: {
742
- marginLeft: 1
743
- },
744
- progressContainer: {
809
+ track: {
745
810
  flex: 1,
746
- gap: 6
811
+ gap: 4
747
812
  },
748
- progressRow: {
813
+ wave: {
814
+ height: WAVE_HEIGHT,
749
815
  flexDirection: 'row',
750
- justifyContent: 'space-between',
751
- alignItems: 'center'
752
- },
753
- progressTrack: {
754
- height: 6,
755
- backgroundColor: '#D0D7E6',
756
- borderRadius: 999,
816
+ alignItems: 'flex-end',
817
+ gap: BAR_GAP,
757
818
  overflow: 'hidden'
758
819
  },
759
- hiddenWebView: {
760
- position: 'absolute',
761
- width: 1,
762
- height: 1,
763
- opacity: 0
820
+ bar: {
821
+ width: BAR_WIDTH,
822
+ borderRadius: BAR_WIDTH / 2
764
823
  },
765
- progressFill: {
766
- height: '100%',
767
- borderRadius: 999
824
+ metaRow: {
825
+ flexDirection: 'row',
826
+ alignItems: 'center',
827
+ gap: 6,
828
+ minHeight: 16
768
829
  },
769
830
  time: {
770
831
  fontSize: 12,
771
- color: '#667085',
772
- fontWeight: '500'
832
+ lineHeight: 16,
833
+ fontVariant: ['tabular-nums'],
834
+ opacity: 0.85
835
+ },
836
+ speed: {
837
+ borderRadius: 8,
838
+ paddingHorizontal: 5,
839
+ paddingVertical: 1,
840
+ opacity: 0.85
841
+ },
842
+ speedText: {
843
+ fontSize: 11,
844
+ lineHeight: 14,
845
+ fontWeight: '700'
773
846
  }
774
847
  });
775
848
  //# sourceMappingURL=AudioMessage.js.map