stream-chat-react-native-core 7.0.0-rc.8 → 7.0.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 (498) hide show
  1. package/README.md +1 -1
  2. package/lib/commonjs/components/Attachment/AudioAttachment.js +19 -9
  3. package/lib/commonjs/components/Attachment/AudioAttachment.js.map +1 -1
  4. package/lib/commonjs/components/Attachment/FileAttachmentGroup.js +3 -1
  5. package/lib/commonjs/components/Attachment/FileAttachmentGroup.js.map +1 -1
  6. package/lib/commonjs/components/Attachment/Gallery.js +16 -3
  7. package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
  8. package/lib/commonjs/components/Attachment/ImageReloadIndicator.js.map +1 -1
  9. package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js +2 -2
  10. package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
  11. package/lib/commonjs/components/AttachmentPicker/components/AttachmentPickerItem.js +7 -24
  12. package/lib/commonjs/components/AttachmentPicker/components/AttachmentPickerItem.js.map +1 -1
  13. package/lib/commonjs/components/AutoCompleteInput/AutoCompleteInput.js +2 -4
  14. package/lib/commonjs/components/AutoCompleteInput/AutoCompleteInput.js.map +1 -1
  15. package/lib/commonjs/components/Channel/Channel.js +320 -310
  16. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  17. package/lib/commonjs/components/Channel/hooks/useChannelDataState.js.map +1 -1
  18. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js +2 -0
  19. package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  20. package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js +133 -147
  21. package/lib/commonjs/components/Channel/hooks/useMessageListPagination.js.map +1 -1
  22. package/lib/commonjs/components/Chat/Chat.js.map +1 -1
  23. package/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js +8 -13
  24. package/lib/commonjs/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
  25. package/lib/commonjs/components/Message/Message.js +14 -7
  26. package/lib/commonjs/components/Message/Message.js.map +1 -1
  27. package/lib/commonjs/components/Message/MessageSimple/MessageFooter.js.map +1 -1
  28. package/lib/commonjs/components/Message/MessageSimple/MessageSimple.js +70 -54
  29. package/lib/commonjs/components/Message/MessageSimple/MessageSimple.js.map +1 -1
  30. package/lib/commonjs/components/Message/MessageSimple/MessageStatus.js +14 -6
  31. package/lib/commonjs/components/Message/MessageSimple/MessageStatus.js.map +1 -1
  32. package/lib/commonjs/components/Message/MessageSimple/MessageTextContainer.js.map +1 -1
  33. package/lib/commonjs/components/Message/MessageSimple/utils/renderText.js.map +1 -1
  34. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js +3 -1
  35. package/lib/commonjs/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  36. package/lib/commonjs/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  37. package/lib/commonjs/components/Message/hooks/useMessageActions.js.map +1 -1
  38. package/lib/commonjs/components/MessageInput/FileUploadPreview.js +25 -39
  39. package/lib/commonjs/components/MessageInput/FileUploadPreview.js.map +1 -1
  40. package/lib/commonjs/components/MessageInput/InputButtons.js +18 -15
  41. package/lib/commonjs/components/MessageInput/InputButtons.js.map +1 -1
  42. package/lib/commonjs/components/MessageInput/MessageInput.js +96 -74
  43. package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
  44. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingButton.js +2 -1
  45. package/lib/commonjs/components/MessageInput/components/AudioRecorder/AudioRecordingButton.js.map +1 -1
  46. package/lib/commonjs/components/MessageInput/hooks/useAudioController.js +4 -4
  47. package/lib/commonjs/components/MessageInput/hooks/useAudioController.js.map +1 -1
  48. package/lib/commonjs/components/MessageList/MessageList.js +281 -223
  49. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  50. package/lib/commonjs/components/MessageList/hooks/useMessageList.js +65 -48
  51. package/lib/commonjs/components/MessageList/hooks/useMessageList.js.map +1 -1
  52. package/lib/commonjs/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.js.map +1 -1
  53. package/lib/commonjs/components/MessageList/utils/getGroupStyles.js +1 -1
  54. package/lib/commonjs/components/MessageList/utils/getGroupStyles.js.map +1 -1
  55. package/lib/commonjs/components/MessageList/utils/getLastReceivedMessage.js.map +1 -1
  56. package/lib/commonjs/components/MessageList/utils/getReadState.js +20 -0
  57. package/lib/commonjs/components/MessageList/utils/getReadState.js.map +1 -0
  58. package/lib/commonjs/components/MessageMenu/hooks/useFetchReactions.js.map +1 -1
  59. package/lib/commonjs/components/Poll/components/Button.js.map +1 -1
  60. package/lib/commonjs/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
  61. package/lib/commonjs/components/Reply/Reply.js +2 -2
  62. package/lib/commonjs/components/Reply/Reply.js.map +1 -1
  63. package/lib/commonjs/components/Thread/components/ThreadFooterComponent.js +24 -6
  64. package/lib/commonjs/components/Thread/components/ThreadFooterComponent.js.map +1 -1
  65. package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -1
  66. package/lib/commonjs/components/UIComponents/BottomSheetModal.js +10 -6
  67. package/lib/commonjs/components/UIComponents/BottomSheetModal.js.map +1 -1
  68. package/lib/commonjs/components/index.js +15 -4
  69. package/lib/commonjs/components/index.js.map +1 -1
  70. package/lib/commonjs/contexts/attachmentPickerContext/AttachmentPickerContext.js.map +1 -1
  71. package/lib/commonjs/contexts/channelsStateContext/useChannelState.js +5 -1
  72. package/lib/commonjs/contexts/channelsStateContext/useChannelState.js.map +1 -1
  73. package/lib/commonjs/contexts/debugContext/DebugContext.js.map +1 -1
  74. package/lib/commonjs/contexts/imageGalleryContext/ImageGalleryContext.js.map +1 -1
  75. package/lib/commonjs/contexts/messageContext/MessageContext.js.map +1 -1
  76. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +535 -523
  77. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  78. package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +7 -6
  79. package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
  80. package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js +37 -41
  81. package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
  82. package/lib/commonjs/contexts/messageInputContext/utils/utils.js +4 -4
  83. package/lib/commonjs/contexts/messageInputContext/utils/utils.js.map +1 -1
  84. package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
  85. package/lib/commonjs/contexts/pollContext/pollContext.js.map +1 -1
  86. package/lib/commonjs/contexts/suggestionsContext/SuggestionsContext.js +23 -14
  87. package/lib/commonjs/contexts/suggestionsContext/SuggestionsContext.js.map +1 -1
  88. package/lib/commonjs/contexts/themeContext/utils/theme.js +2 -1
  89. package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
  90. package/lib/commonjs/contexts/threadContext/ThreadContext.js.map +1 -1
  91. package/lib/commonjs/contexts/threadsContext/ThreadListItemContext.js.map +1 -1
  92. package/lib/commonjs/hooks/index.js +22 -0
  93. package/lib/commonjs/hooks/index.js.map +1 -1
  94. package/lib/commonjs/hooks/useAudioPlayer.js +60 -69
  95. package/lib/commonjs/hooks/useAudioPlayer.js.map +1 -1
  96. package/lib/commonjs/hooks/useStableCallback.js +13 -0
  97. package/lib/commonjs/hooks/useStableCallback.js.map +1 -0
  98. package/lib/commonjs/hooks/useTranslatedMessage.js.map +1 -1
  99. package/lib/commonjs/i18n/en.json +3 -0
  100. package/lib/commonjs/i18n/es.json +3 -0
  101. package/lib/commonjs/i18n/fr.json +3 -0
  102. package/lib/commonjs/i18n/he.json +3 -0
  103. package/lib/commonjs/i18n/hi.json +2 -0
  104. package/lib/commonjs/i18n/it.json +3 -0
  105. package/lib/commonjs/i18n/ja.json +1 -0
  106. package/lib/commonjs/i18n/ko.json +1 -0
  107. package/lib/commonjs/i18n/nl.json +2 -0
  108. package/lib/commonjs/i18n/pt-br.json +3 -0
  109. package/lib/commonjs/i18n/ru.json +4 -0
  110. package/lib/commonjs/i18n/tr.json +2 -0
  111. package/lib/commonjs/mock-builders/api/channelMocks.js.map +1 -1
  112. package/lib/commonjs/native.js.map +1 -1
  113. package/lib/commonjs/store/mappers/mapMessageToStorable.js.map +1 -1
  114. package/lib/commonjs/types/types.js.map +1 -1
  115. package/lib/commonjs/utils/compressImage.js +1 -1
  116. package/lib/commonjs/utils/compressImage.js.map +1 -1
  117. package/lib/commonjs/utils/removeReservedFields.js.map +1 -1
  118. package/lib/commonjs/utils/utils.js +17 -4
  119. package/lib/commonjs/utils/utils.js.map +1 -1
  120. package/lib/commonjs/version.json +1 -1
  121. package/lib/module/components/Attachment/AudioAttachment.js +19 -9
  122. package/lib/module/components/Attachment/AudioAttachment.js.map +1 -1
  123. package/lib/module/components/Attachment/FileAttachmentGroup.js +3 -1
  124. package/lib/module/components/Attachment/FileAttachmentGroup.js.map +1 -1
  125. package/lib/module/components/Attachment/Gallery.js +16 -3
  126. package/lib/module/components/Attachment/Gallery.js.map +1 -1
  127. package/lib/module/components/Attachment/ImageReloadIndicator.js.map +1 -1
  128. package/lib/module/components/AttachmentPicker/AttachmentPicker.js +2 -2
  129. package/lib/module/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
  130. package/lib/module/components/AttachmentPicker/components/AttachmentPickerItem.js +7 -24
  131. package/lib/module/components/AttachmentPicker/components/AttachmentPickerItem.js.map +1 -1
  132. package/lib/module/components/AutoCompleteInput/AutoCompleteInput.js +2 -4
  133. package/lib/module/components/AutoCompleteInput/AutoCompleteInput.js.map +1 -1
  134. package/lib/module/components/Channel/Channel.js +320 -310
  135. package/lib/module/components/Channel/Channel.js.map +1 -1
  136. package/lib/module/components/Channel/hooks/useChannelDataState.js.map +1 -1
  137. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js +2 -0
  138. package/lib/module/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
  139. package/lib/module/components/Channel/hooks/useMessageListPagination.js +133 -147
  140. package/lib/module/components/Channel/hooks/useMessageListPagination.js.map +1 -1
  141. package/lib/module/components/Chat/Chat.js.map +1 -1
  142. package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js +8 -13
  143. package/lib/module/components/KeyboardCompatibleView/KeyboardCompatibleView.js.map +1 -1
  144. package/lib/module/components/Message/Message.js +14 -7
  145. package/lib/module/components/Message/Message.js.map +1 -1
  146. package/lib/module/components/Message/MessageSimple/MessageFooter.js.map +1 -1
  147. package/lib/module/components/Message/MessageSimple/MessageSimple.js +70 -54
  148. package/lib/module/components/Message/MessageSimple/MessageSimple.js.map +1 -1
  149. package/lib/module/components/Message/MessageSimple/MessageStatus.js +14 -6
  150. package/lib/module/components/Message/MessageSimple/MessageStatus.js.map +1 -1
  151. package/lib/module/components/Message/MessageSimple/MessageTextContainer.js.map +1 -1
  152. package/lib/module/components/Message/MessageSimple/utils/renderText.js.map +1 -1
  153. package/lib/module/components/Message/hooks/useCreateMessageContext.js +3 -1
  154. package/lib/module/components/Message/hooks/useCreateMessageContext.js.map +1 -1
  155. package/lib/module/components/Message/hooks/useMessageActionHandlers.js.map +1 -1
  156. package/lib/module/components/Message/hooks/useMessageActions.js.map +1 -1
  157. package/lib/module/components/MessageInput/FileUploadPreview.js +25 -39
  158. package/lib/module/components/MessageInput/FileUploadPreview.js.map +1 -1
  159. package/lib/module/components/MessageInput/InputButtons.js +18 -15
  160. package/lib/module/components/MessageInput/InputButtons.js.map +1 -1
  161. package/lib/module/components/MessageInput/MessageInput.js +96 -74
  162. package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
  163. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingButton.js +2 -1
  164. package/lib/module/components/MessageInput/components/AudioRecorder/AudioRecordingButton.js.map +1 -1
  165. package/lib/module/components/MessageInput/hooks/useAudioController.js +4 -4
  166. package/lib/module/components/MessageInput/hooks/useAudioController.js.map +1 -1
  167. package/lib/module/components/MessageList/MessageList.js +281 -223
  168. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  169. package/lib/module/components/MessageList/hooks/useMessageList.js +65 -48
  170. package/lib/module/components/MessageList/hooks/useMessageList.js.map +1 -1
  171. package/lib/module/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.js.map +1 -1
  172. package/lib/module/components/MessageList/utils/getGroupStyles.js +1 -1
  173. package/lib/module/components/MessageList/utils/getGroupStyles.js.map +1 -1
  174. package/lib/module/components/MessageList/utils/getLastReceivedMessage.js.map +1 -1
  175. package/lib/module/components/MessageList/utils/getReadState.js +20 -0
  176. package/lib/module/components/MessageList/utils/getReadState.js.map +1 -0
  177. package/lib/module/components/MessageMenu/hooks/useFetchReactions.js.map +1 -1
  178. package/lib/module/components/Poll/components/Button.js.map +1 -1
  179. package/lib/module/components/Poll/components/PollResults/PollResultItem.js.map +1 -1
  180. package/lib/module/components/Reply/Reply.js +2 -2
  181. package/lib/module/components/Reply/Reply.js.map +1 -1
  182. package/lib/module/components/Thread/components/ThreadFooterComponent.js +24 -6
  183. package/lib/module/components/Thread/components/ThreadFooterComponent.js.map +1 -1
  184. package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -1
  185. package/lib/module/components/UIComponents/BottomSheetModal.js +10 -6
  186. package/lib/module/components/UIComponents/BottomSheetModal.js.map +1 -1
  187. package/lib/module/components/index.js +15 -4
  188. package/lib/module/components/index.js.map +1 -1
  189. package/lib/module/contexts/attachmentPickerContext/AttachmentPickerContext.js.map +1 -1
  190. package/lib/module/contexts/channelsStateContext/useChannelState.js +5 -1
  191. package/lib/module/contexts/channelsStateContext/useChannelState.js.map +1 -1
  192. package/lib/module/contexts/debugContext/DebugContext.js.map +1 -1
  193. package/lib/module/contexts/imageGalleryContext/ImageGalleryContext.js.map +1 -1
  194. package/lib/module/contexts/messageContext/MessageContext.js.map +1 -1
  195. package/lib/module/contexts/messageInputContext/MessageInputContext.js +535 -523
  196. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  197. package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +7 -6
  198. package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
  199. package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js +37 -41
  200. package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
  201. package/lib/module/contexts/messageInputContext/utils/utils.js +4 -4
  202. package/lib/module/contexts/messageInputContext/utils/utils.js.map +1 -1
  203. package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
  204. package/lib/module/contexts/pollContext/pollContext.js.map +1 -1
  205. package/lib/module/contexts/suggestionsContext/SuggestionsContext.js +23 -14
  206. package/lib/module/contexts/suggestionsContext/SuggestionsContext.js.map +1 -1
  207. package/lib/module/contexts/themeContext/utils/theme.js +2 -1
  208. package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
  209. package/lib/module/contexts/threadContext/ThreadContext.js.map +1 -1
  210. package/lib/module/contexts/threadsContext/ThreadListItemContext.js.map +1 -1
  211. package/lib/module/hooks/index.js +22 -0
  212. package/lib/module/hooks/index.js.map +1 -1
  213. package/lib/module/hooks/useAudioPlayer.js +60 -69
  214. package/lib/module/hooks/useAudioPlayer.js.map +1 -1
  215. package/lib/module/hooks/useStableCallback.js +13 -0
  216. package/lib/module/hooks/useStableCallback.js.map +1 -0
  217. package/lib/module/hooks/useTranslatedMessage.js.map +1 -1
  218. package/lib/module/i18n/en.json +3 -0
  219. package/lib/module/i18n/es.json +3 -0
  220. package/lib/module/i18n/fr.json +3 -0
  221. package/lib/module/i18n/he.json +3 -0
  222. package/lib/module/i18n/hi.json +2 -0
  223. package/lib/module/i18n/it.json +3 -0
  224. package/lib/module/i18n/ja.json +1 -0
  225. package/lib/module/i18n/ko.json +1 -0
  226. package/lib/module/i18n/nl.json +2 -0
  227. package/lib/module/i18n/pt-br.json +3 -0
  228. package/lib/module/i18n/ru.json +4 -0
  229. package/lib/module/i18n/tr.json +2 -0
  230. package/lib/module/mock-builders/api/channelMocks.js.map +1 -1
  231. package/lib/module/native.js.map +1 -1
  232. package/lib/module/store/mappers/mapMessageToStorable.js.map +1 -1
  233. package/lib/module/types/types.js.map +1 -1
  234. package/lib/module/utils/compressImage.js +1 -1
  235. package/lib/module/utils/compressImage.js.map +1 -1
  236. package/lib/module/utils/removeReservedFields.js.map +1 -1
  237. package/lib/module/utils/utils.js +17 -4
  238. package/lib/module/utils/utils.js.map +1 -1
  239. package/lib/module/version.json +1 -1
  240. package/lib/typescript/components/Attachment/AudioAttachment.d.ts +2 -2
  241. package/lib/typescript/components/Attachment/AudioAttachment.d.ts.map +1 -1
  242. package/lib/typescript/components/Attachment/FileAttachmentGroup.d.ts.map +1 -1
  243. package/lib/typescript/components/Attachment/Gallery.d.ts +3 -3
  244. package/lib/typescript/components/Attachment/Gallery.d.ts.map +1 -1
  245. package/lib/typescript/components/Attachment/ImageReloadIndicator.d.ts +4 -3
  246. package/lib/typescript/components/Attachment/ImageReloadIndicator.d.ts.map +1 -1
  247. package/lib/typescript/components/AttachmentPicker/AttachmentPicker.d.ts.map +1 -1
  248. package/lib/typescript/components/AttachmentPicker/components/AttachmentPickerItem.d.ts +2 -2
  249. package/lib/typescript/components/AttachmentPicker/components/AttachmentPickerItem.d.ts.map +1 -1
  250. package/lib/typescript/components/AutoCompleteInput/AutoCompleteInput.d.ts +2 -3
  251. package/lib/typescript/components/AutoCompleteInput/AutoCompleteInput.d.ts.map +1 -1
  252. package/lib/typescript/components/Channel/Channel.d.ts +3 -4
  253. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  254. package/lib/typescript/components/Channel/hooks/useChannelDataState.d.ts +2 -3
  255. package/lib/typescript/components/Channel/hooks/useChannelDataState.d.ts.map +1 -1
  256. package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts +1 -1
  257. package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts.map +1 -1
  258. package/lib/typescript/components/Channel/hooks/useCreateThreadContext.d.ts +6 -6
  259. package/lib/typescript/components/Channel/hooks/useCreateThreadContext.d.ts.map +1 -1
  260. package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts +3 -3
  261. package/lib/typescript/components/Channel/hooks/useMessageListPagination.d.ts.map +1 -1
  262. package/lib/typescript/components/ChannelPreview/hooks/useChannelPreviewData.d.ts +1 -1
  263. package/lib/typescript/components/ChannelPreview/hooks/useLatestMessagePreview.d.ts +1 -1
  264. package/lib/typescript/components/Chat/Chat.d.ts +0 -9
  265. package/lib/typescript/components/Chat/Chat.d.ts.map +1 -1
  266. package/lib/typescript/components/KeyboardCompatibleView/KeyboardCompatibleView.d.ts +3 -0
  267. package/lib/typescript/components/KeyboardCompatibleView/KeyboardCompatibleView.d.ts.map +1 -1
  268. package/lib/typescript/components/Message/Message.d.ts +4 -5
  269. package/lib/typescript/components/Message/Message.d.ts.map +1 -1
  270. package/lib/typescript/components/Message/MessageSimple/MessageFooter.d.ts +2 -3
  271. package/lib/typescript/components/Message/MessageSimple/MessageFooter.d.ts.map +1 -1
  272. package/lib/typescript/components/Message/MessageSimple/MessageSimple.d.ts.map +1 -1
  273. package/lib/typescript/components/Message/MessageSimple/MessageStatus.d.ts +1 -1
  274. package/lib/typescript/components/Message/MessageSimple/MessageStatus.d.ts.map +1 -1
  275. package/lib/typescript/components/Message/MessageSimple/MessageTextContainer.d.ts.map +1 -1
  276. package/lib/typescript/components/Message/MessageSimple/utils/renderText.d.ts +2 -2
  277. package/lib/typescript/components/Message/MessageSimple/utils/renderText.d.ts.map +1 -1
  278. package/lib/typescript/components/Message/hooks/useCreateMessageContext.d.ts +1 -1
  279. package/lib/typescript/components/Message/hooks/useCreateMessageContext.d.ts.map +1 -1
  280. package/lib/typescript/components/Message/hooks/useMessageActionHandlers.d.ts.map +1 -1
  281. package/lib/typescript/components/Message/hooks/useMessageActions.d.ts +2 -2
  282. package/lib/typescript/components/Message/hooks/useMessageActions.d.ts.map +1 -1
  283. package/lib/typescript/components/MessageInput/FileUploadPreview.d.ts +2 -4
  284. package/lib/typescript/components/MessageInput/FileUploadPreview.d.ts.map +1 -1
  285. package/lib/typescript/components/MessageInput/InputButtons.d.ts +1 -1
  286. package/lib/typescript/components/MessageInput/InputButtons.d.ts.map +1 -1
  287. package/lib/typescript/components/MessageInput/MessageInput.d.ts +1 -1
  288. package/lib/typescript/components/MessageInput/MessageInput.d.ts.map +1 -1
  289. package/lib/typescript/components/MessageInput/hooks/useAudioController.d.ts.map +1 -1
  290. package/lib/typescript/components/MessageList/MessageList.d.ts +4 -4
  291. package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
  292. package/lib/typescript/components/MessageList/MessageSystem.d.ts +2 -2
  293. package/lib/typescript/components/MessageList/MessageSystem.d.ts.map +1 -1
  294. package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts +14 -9
  295. package/lib/typescript/components/MessageList/hooks/useMessageList.d.ts.map +1 -1
  296. package/lib/typescript/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.d.ts +2 -2
  297. package/lib/typescript/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.d.ts.map +1 -1
  298. package/lib/typescript/components/MessageList/utils/getGroupStyles.d.ts.map +1 -1
  299. package/lib/typescript/components/MessageList/utils/getLastReceivedMessage.d.ts +2 -2
  300. package/lib/typescript/components/MessageList/utils/getLastReceivedMessage.d.ts.map +1 -1
  301. package/lib/typescript/components/MessageList/utils/getReadState.d.ts +9 -0
  302. package/lib/typescript/components/MessageList/utils/getReadState.d.ts.map +1 -0
  303. package/lib/typescript/components/MessageMenu/hooks/useFetchReactions.d.ts +2 -3
  304. package/lib/typescript/components/MessageMenu/hooks/useFetchReactions.d.ts.map +1 -1
  305. package/lib/typescript/components/Poll/components/Button.d.ts +2 -3
  306. package/lib/typescript/components/Poll/components/Button.d.ts.map +1 -1
  307. package/lib/typescript/components/Poll/components/PollResults/PollResultItem.d.ts +2 -3
  308. package/lib/typescript/components/Poll/components/PollResults/PollResultItem.d.ts.map +1 -1
  309. package/lib/typescript/components/Reply/Reply.d.ts.map +1 -1
  310. package/lib/typescript/components/Thread/components/ThreadFooterComponent.d.ts.map +1 -1
  311. package/lib/typescript/components/ThreadList/ThreadListItem.d.ts.map +1 -1
  312. package/lib/typescript/components/UIComponents/BottomSheetModal.d.ts.map +1 -1
  313. package/lib/typescript/components/index.d.ts +2 -1
  314. package/lib/typescript/components/index.d.ts.map +1 -1
  315. package/lib/typescript/contexts/attachmentPickerContext/AttachmentPickerContext.d.ts +3 -3
  316. package/lib/typescript/contexts/attachmentPickerContext/AttachmentPickerContext.d.ts.map +1 -1
  317. package/lib/typescript/contexts/channelsStateContext/useChannelState.d.ts.map +1 -1
  318. package/lib/typescript/contexts/debugContext/DebugContext.d.ts +2 -3
  319. package/lib/typescript/contexts/debugContext/DebugContext.d.ts.map +1 -1
  320. package/lib/typescript/contexts/imageGalleryContext/ImageGalleryContext.d.ts +3 -3
  321. package/lib/typescript/contexts/imageGalleryContext/ImageGalleryContext.d.ts.map +1 -1
  322. package/lib/typescript/contexts/messageContext/MessageContext.d.ts +6 -4
  323. package/lib/typescript/contexts/messageContext/MessageContext.d.ts.map +1 -1
  324. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +13 -15
  325. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
  326. package/lib/typescript/contexts/messageInputContext/hooks/useCreateMessageInputContext.d.ts +1 -1
  327. package/lib/typescript/contexts/messageInputContext/hooks/useCreateMessageInputContext.d.ts.map +1 -1
  328. package/lib/typescript/contexts/messageInputContext/hooks/useMessageDetailsForState.d.ts +3 -3
  329. package/lib/typescript/contexts/messageInputContext/hooks/useMessageDetailsForState.d.ts.map +1 -1
  330. package/lib/typescript/contexts/messageInputContext/utils/utils.d.ts +2 -2
  331. package/lib/typescript/contexts/messageInputContext/utils/utils.d.ts.map +1 -1
  332. package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +25 -21
  333. package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts.map +1 -1
  334. package/lib/typescript/contexts/pollContext/pollContext.d.ts +2 -3
  335. package/lib/typescript/contexts/pollContext/pollContext.d.ts.map +1 -1
  336. package/lib/typescript/contexts/suggestionsContext/SuggestionsContext.d.ts.map +1 -1
  337. package/lib/typescript/contexts/themeContext/utils/theme.d.ts +1 -0
  338. package/lib/typescript/contexts/themeContext/utils/theme.d.ts.map +1 -1
  339. package/lib/typescript/contexts/threadContext/ThreadContext.d.ts +4 -5
  340. package/lib/typescript/contexts/threadContext/ThreadContext.d.ts.map +1 -1
  341. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts +3 -4
  342. package/lib/typescript/contexts/threadsContext/ThreadListItemContext.d.ts.map +1 -1
  343. package/lib/typescript/hooks/index.d.ts +2 -0
  344. package/lib/typescript/hooks/index.d.ts.map +1 -1
  345. package/lib/typescript/hooks/useAudioPlayer.d.ts.map +1 -1
  346. package/lib/typescript/hooks/useStableCallback.d.ts +26 -0
  347. package/lib/typescript/hooks/useStableCallback.d.ts.map +1 -0
  348. package/lib/typescript/hooks/useTranslatedMessage.d.ts +59 -58
  349. package/lib/typescript/hooks/useTranslatedMessage.d.ts.map +1 -1
  350. package/lib/typescript/i18n/en.json +3 -0
  351. package/lib/typescript/i18n/es.json +3 -0
  352. package/lib/typescript/i18n/fr.json +3 -0
  353. package/lib/typescript/i18n/he.json +3 -0
  354. package/lib/typescript/i18n/hi.json +2 -0
  355. package/lib/typescript/i18n/it.json +3 -0
  356. package/lib/typescript/i18n/ja.json +1 -0
  357. package/lib/typescript/i18n/ko.json +1 -0
  358. package/lib/typescript/i18n/nl.json +2 -0
  359. package/lib/typescript/i18n/pt-br.json +3 -0
  360. package/lib/typescript/i18n/ru.json +4 -0
  361. package/lib/typescript/i18n/tr.json +2 -0
  362. package/lib/typescript/native.d.ts +8 -11
  363. package/lib/typescript/native.d.ts.map +1 -1
  364. package/lib/typescript/store/apis/insertReaction.d.ts +2 -2
  365. package/lib/typescript/store/apis/insertReaction.d.ts.map +1 -1
  366. package/lib/typescript/store/apis/updateMessage.d.ts +2 -2
  367. package/lib/typescript/store/apis/updateMessage.d.ts.map +1 -1
  368. package/lib/typescript/store/apis/updateReaction.d.ts +2 -2
  369. package/lib/typescript/store/apis/updateReaction.d.ts.map +1 -1
  370. package/lib/typescript/store/mappers/mapMessageToStorable.d.ts +2 -2
  371. package/lib/typescript/store/mappers/mapMessageToStorable.d.ts.map +1 -1
  372. package/lib/typescript/types/types.d.ts +28 -50
  373. package/lib/typescript/types/types.d.ts.map +1 -1
  374. package/lib/typescript/utils/compressImage.d.ts +2 -2
  375. package/lib/typescript/utils/compressImage.d.ts.map +1 -1
  376. package/lib/typescript/utils/i18n/Streami18n.d.ts +3 -0
  377. package/lib/typescript/utils/i18n/Streami18n.d.ts.map +1 -1
  378. package/lib/typescript/utils/removeReservedFields.d.ts +2 -3
  379. package/lib/typescript/utils/removeReservedFields.d.ts.map +1 -1
  380. package/lib/typescript/utils/utils.d.ts +10 -10
  381. package/lib/typescript/utils/utils.d.ts.map +1 -1
  382. package/package.json +4 -3
  383. package/src/components/Attachment/AudioAttachment.tsx +12 -6
  384. package/src/components/Attachment/FileAttachmentGroup.tsx +3 -1
  385. package/src/components/Attachment/Gallery.tsx +22 -7
  386. package/src/components/Attachment/ImageReloadIndicator.tsx +4 -5
  387. package/src/components/AttachmentPicker/AttachmentPicker.tsx +4 -10
  388. package/src/components/AttachmentPicker/components/AttachmentPickerItem.tsx +9 -37
  389. package/src/components/AutoCompleteInput/AutoCompleteInput.tsx +27 -23
  390. package/src/components/AutoCompleteInput/__tests__/AutoCompleteInput.test.js +12 -12
  391. package/src/components/Channel/Channel.tsx +423 -361
  392. package/src/components/Channel/__tests__/Channel.test.js +10 -3
  393. package/src/components/Channel/hooks/useChannelDataState.ts +2 -4
  394. package/src/components/Channel/hooks/useCreateMessagesContext.ts +2 -0
  395. package/src/components/Channel/hooks/useMessageListPagination.tsx +150 -150
  396. package/src/components/Chat/Chat.tsx +0 -9
  397. package/src/components/ImageGallery/__tests__/ImageGallery.test.tsx +8 -7
  398. package/src/components/ImageGallery/__tests__/ImageGalleryFooter.test.tsx +5 -4
  399. package/src/components/ImageGallery/__tests__/ImageGalleryHeader.test.tsx +5 -4
  400. package/src/components/ImageGallery/__tests__/ImageGalleryOverlay.test.tsx +3 -2
  401. package/src/components/KeyboardCompatibleView/KeyboardCompatibleView.tsx +12 -5
  402. package/src/components/Message/Message.tsx +16 -15
  403. package/src/components/Message/MessageSimple/MessageFooter.tsx +2 -3
  404. package/src/components/Message/MessageSimple/MessageSimple.tsx +112 -70
  405. package/src/components/Message/MessageSimple/MessageStatus.tsx +19 -17
  406. package/src/components/Message/MessageSimple/MessageTextContainer.tsx +3 -3
  407. package/src/components/Message/MessageSimple/__tests__/MessageStatus.test.js +11 -5
  408. package/src/components/Message/MessageSimple/__tests__/MessageTextContainer.test.tsx +5 -4
  409. package/src/components/Message/MessageSimple/utils/renderText.tsx +2 -3
  410. package/src/components/Message/hooks/useCreateMessageContext.ts +3 -0
  411. package/src/components/Message/hooks/useMessageActionHandlers.ts +1 -3
  412. package/src/components/Message/hooks/useMessageActions.tsx +4 -3
  413. package/src/components/MessageInput/FileUploadPreview.tsx +41 -70
  414. package/src/components/MessageInput/ImageUploadPreview.tsx +2 -2
  415. package/src/components/MessageInput/InputButtons.tsx +14 -10
  416. package/src/components/MessageInput/MessageInput.tsx +28 -30
  417. package/src/components/MessageInput/__tests__/FileUploadPreview.test.js +1 -1
  418. package/src/components/MessageInput/__tests__/MessageInput.test.js +3 -1
  419. package/src/components/MessageInput/components/AudioRecorder/AudioRecordingButton.tsx +1 -0
  420. package/src/components/MessageInput/hooks/useAudioController.tsx +6 -5
  421. package/src/components/MessageList/MessageList.tsx +253 -137
  422. package/src/components/MessageList/MessageSystem.tsx +2 -2
  423. package/src/components/MessageList/hooks/useMessageList.ts +82 -64
  424. package/src/components/MessageList/hooks/useShouldScrollToRecentOnNewOwnMessage.ts +2 -2
  425. package/src/components/MessageList/utils/getGroupStyles.ts +7 -5
  426. package/src/components/MessageList/utils/getLastReceivedMessage.ts +3 -3
  427. package/src/components/MessageList/utils/getReadState.ts +27 -0
  428. package/src/components/MessageMenu/__tests__/MessageUserReactions.test.tsx +2 -3
  429. package/src/components/MessageMenu/hooks/useFetchReactions.ts +2 -3
  430. package/src/components/Poll/components/Button.tsx +2 -4
  431. package/src/components/Poll/components/PollResults/PollResultItem.tsx +2 -3
  432. package/src/components/Reply/Reply.tsx +1 -2
  433. package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +318 -847
  434. package/src/components/Thread/components/ThreadFooterComponent.tsx +31 -9
  435. package/src/components/ThreadList/ThreadListItem.tsx +6 -4
  436. package/src/components/UIComponents/BottomSheetModal.tsx +11 -7
  437. package/src/components/index.ts +2 -1
  438. package/src/contexts/attachmentPickerContext/AttachmentPickerContext.tsx +5 -4
  439. package/src/contexts/channelsStateContext/useChannelState.ts +5 -1
  440. package/src/contexts/debugContext/DebugContext.tsx +2 -4
  441. package/src/contexts/imageGalleryContext/ImageGalleryContext.tsx +5 -4
  442. package/src/contexts/messageContext/MessageContext.tsx +6 -4
  443. package/src/contexts/messageInputContext/MessageInputContext.tsx +295 -262
  444. package/src/contexts/messageInputContext/__tests__/__snapshots__/sendMessage.test.tsx.snap +10 -6
  445. package/src/contexts/messageInputContext/__tests__/sendMessage.test.tsx +6 -5
  446. package/src/contexts/messageInputContext/__tests__/updateMessage.test.tsx +3 -5
  447. package/src/contexts/messageInputContext/__tests__/useMessageDetailsForState.test.tsx +14 -9
  448. package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts +7 -3
  449. package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts +37 -40
  450. package/src/contexts/messageInputContext/utils/utils.ts +6 -8
  451. package/src/contexts/messagesContext/MessagesContext.tsx +26 -20
  452. package/src/contexts/pollContext/pollContext.tsx +2 -4
  453. package/src/contexts/suggestionsContext/SuggestionsContext.tsx +33 -14
  454. package/src/contexts/themeContext/utils/theme.ts +2 -0
  455. package/src/contexts/threadContext/ThreadContext.tsx +4 -6
  456. package/src/contexts/threadsContext/ThreadListItemContext.tsx +3 -5
  457. package/src/hooks/index.ts +2 -0
  458. package/src/hooks/useAudioPlayer.ts +37 -31
  459. package/src/hooks/useStableCallback.ts +37 -0
  460. package/src/hooks/useTranslatedMessage.ts +2 -2
  461. package/src/i18n/en.json +3 -0
  462. package/src/i18n/es.json +3 -0
  463. package/src/i18n/fr.json +3 -0
  464. package/src/i18n/he.json +3 -0
  465. package/src/i18n/hi.json +2 -0
  466. package/src/i18n/it.json +3 -0
  467. package/src/i18n/ja.json +1 -0
  468. package/src/i18n/ko.json +1 -0
  469. package/src/i18n/nl.json +2 -0
  470. package/src/i18n/pt-br.json +3 -0
  471. package/src/i18n/ru.json +4 -0
  472. package/src/i18n/tr.json +2 -0
  473. package/src/mock-builders/api/channelMocks.tsx +2 -8
  474. package/src/native.ts +13 -8
  475. package/src/store/apis/insertReaction.ts +2 -2
  476. package/src/store/apis/updateMessage.ts +2 -2
  477. package/src/store/apis/updateReaction.ts +2 -2
  478. package/src/store/mappers/mapMessageToStorable.ts +2 -2
  479. package/src/types/stream-chat-common-custom-data.d.ts +22 -22
  480. package/src/types/types.ts +35 -54
  481. package/src/utils/compressImage.ts +3 -4
  482. package/src/utils/removeReservedFields.ts +3 -5
  483. package/src/utils/utils.ts +25 -16
  484. package/src/version.json +1 -1
  485. package/lib/commonjs/components/MessageList/hooks/useLastReadData.js +0 -20
  486. package/lib/commonjs/components/MessageList/hooks/useLastReadData.js.map +0 -1
  487. package/lib/commonjs/components/MessageList/utils/getReadStates.js +0 -34
  488. package/lib/commonjs/components/MessageList/utils/getReadStates.js.map +0 -1
  489. package/lib/module/components/MessageList/hooks/useLastReadData.js +0 -20
  490. package/lib/module/components/MessageList/hooks/useLastReadData.js.map +0 -1
  491. package/lib/module/components/MessageList/utils/getReadStates.js +0 -34
  492. package/lib/module/components/MessageList/utils/getReadStates.js.map +0 -1
  493. package/lib/typescript/components/MessageList/hooks/useLastReadData.d.ts +0 -12
  494. package/lib/typescript/components/MessageList/hooks/useLastReadData.d.ts.map +0 -1
  495. package/lib/typescript/components/MessageList/utils/getReadStates.d.ts +0 -5
  496. package/lib/typescript/components/MessageList/utils/getReadStates.d.ts.map +0 -1
  497. package/src/components/MessageList/hooks/useLastReadData.ts +0 -29
  498. package/src/components/MessageList/utils/getReadStates.ts +0 -55
@@ -11,7 +11,7 @@ import {
11
11
  ChannelState,
12
12
  Channel as ChannelType,
13
13
  EventHandler,
14
- FormatMessageResponse,
14
+ LocalMessage,
15
15
  MessageLabel,
16
16
  MessageResponse,
17
17
  Reaction,
@@ -75,6 +75,7 @@ import {
75
75
  useTranslationContext,
76
76
  } from '../../contexts/translationContext/TranslationContext';
77
77
  import { TypingProvider } from '../../contexts/typingContext/TypingContext';
78
+ import { useStableCallback } from '../../hooks';
78
79
  import { useAppStateListener } from '../../hooks/useAppStateListener';
79
80
 
80
81
  import {
@@ -118,6 +119,7 @@ import { Gallery as GalleryDefault } from '../Attachment/Gallery';
118
119
  import { Giphy as GiphyDefault } from '../Attachment/Giphy';
119
120
  import { ImageLoadingFailedIndicator as ImageLoadingFailedIndicatorDefault } from '../Attachment/ImageLoadingFailedIndicator';
120
121
  import { ImageLoadingIndicator as ImageLoadingIndicatorDefault } from '../Attachment/ImageLoadingIndicator';
122
+ import { ImageReloadIndicator as ImageReloadIndicatorDefault } from '../Attachment/ImageReloadIndicator';
121
123
  import { VideoThumbnail as VideoThumbnailDefault } from '../Attachment/VideoThumbnail';
122
124
  import { AutoCompleteSuggestionHeader as AutoCompleteSuggestionHeaderDefault } from '../AutoCompleteInput/AutoCompleteSuggestionHeader';
123
125
  import { AutoCompleteSuggestionItem as AutoCompleteSuggestionItemDefault } from '../AutoCompleteInput/AutoCompleteSuggestionItem';
@@ -169,7 +171,6 @@ import { ShowThreadMessageInChannelButton as ShowThreadMessageInChannelButtonDef
169
171
  import { StopMessageStreamingButton as DefaultStopMessageStreamingButton } from '../MessageInput/StopMessageStreamingButton';
170
172
  import { UploadProgressIndicator as UploadProgressIndicatorDefault } from '../MessageInput/UploadProgressIndicator';
171
173
  import { DateHeader as DateHeaderDefault } from '../MessageList/DateHeader';
172
- import type { MessageType } from '../MessageList/hooks/useMessageList';
173
174
  import { InlineDateSeparator as InlineDateSeparatorDefault } from '../MessageList/InlineDateSeparator';
174
175
  import { InlineUnreadIndicator as InlineUnreadIndicatorDefault } from '../MessageList/InlineUnreadIndicator';
175
176
  import { MessageList as MessageListDefault } from '../MessageList/MessageList';
@@ -320,6 +321,7 @@ export type ChannelPropsWithContext = Pick<ChannelContextValue, 'channel'> &
320
321
  | 'legacyImageViewerSwipeBehaviour'
321
322
  | 'ImageLoadingFailedIndicator'
322
323
  | 'ImageLoadingIndicator'
324
+ | 'ImageReloadIndicator'
323
325
  | 'markdownRules'
324
326
  | 'Message'
325
327
  | 'MessageActionList'
@@ -551,7 +553,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
551
553
  handleRetry,
552
554
  handleThreadReply,
553
555
  hasCameraPicker = isImagePickerAvailable(),
554
- hasCommands = true,
556
+ hasCommands,
555
557
  hasCreatePoll,
556
558
  // If pickDocument isn't available, default to hiding the file picker
557
559
  hasFilePicker = isDocumentPickerAvailable(),
@@ -560,6 +562,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
560
562
  hideStickyDateHeader = false,
561
563
  ImageLoadingFailedIndicator = ImageLoadingFailedIndicatorDefault,
562
564
  ImageLoadingIndicator = ImageLoadingIndicatorDefault,
565
+ ImageReloadIndicator = ImageReloadIndicatorDefault,
563
566
  ImageUploadPreview = ImageUploadPreviewDefault,
564
567
  initialScrollToFirstUnreadMessage = false,
565
568
  initialValue,
@@ -684,11 +687,12 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
684
687
  },
685
688
  } = useTheme();
686
689
  const [deleted, setDeleted] = useState<boolean>(false);
687
- const [editing, setEditing] = useState<MessageType | undefined>(undefined);
690
+ const [editing, setEditing] = useState<LocalMessage | undefined>(undefined);
688
691
  const [error, setError] = useState<Error | boolean>(false);
689
- const [lastRead, setLastRead] = useState<ChannelContextValue['lastRead']>();
690
- const [quotedMessage, setQuotedMessage] = useState<MessageType | undefined>(undefined);
691
- const [thread, setThread] = useState<MessageType | null>(threadProps || null);
692
+ const [lastRead, setLastRead] = useState<Date | undefined>();
693
+
694
+ const [quotedMessage, setQuotedMessage] = useState<LocalMessage | undefined>(undefined);
695
+ const [thread, setThread] = useState<LocalMessage | null>(threadProps || null);
692
696
  const [threadHasMore, setThreadHasMore] = useState(true);
693
697
  const [threadLoadingMore, setThreadLoadingMore] = useState(false);
694
698
  const [channelUnreadState, setChannelUnreadState] = useState<ChannelUnreadState | undefined>(
@@ -705,6 +709,12 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
705
709
  * Its a map of filename to AbortController
706
710
  */
707
711
  const uploadAbortControllerRef = useRef<Map<string, AbortController>>(new Map());
712
+ /**
713
+ * This ref keeps track of message IDs which have already been optimistically updated.
714
+ * We need it to make sure we don't react on message.new/notification.message_new events
715
+ * if this is indeed the case, as it's a full list update for nothing.
716
+ */
717
+ const optimisticallyUpdatedNewMessages = useMemo<Set<string>>(() => new Set(), []);
708
718
 
709
719
  const channelId = channel?.id || '';
710
720
  const pollCreationEnabled = !channel.disconnected && !!channel?.id && channel?.getConfig()?.polls;
@@ -730,14 +740,33 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
730
740
  channel,
731
741
  });
732
742
 
733
- /**
734
- * Since we copy the current channel state all together, we need to find the greatest time among the below two and apply it as the throttling time for copying the channel state.
735
- * This is done until we remove the newMessageStateUpdateThrottleInterval prop.
736
- */
737
- const copyChannelStateThrottlingTime =
738
- newMessageStateUpdateThrottleInterval > stateUpdateThrottleInterval
739
- ? newMessageStateUpdateThrottleInterval
740
- : stateUpdateThrottleInterval;
743
+ const setReadThrottled = useMemo(
744
+ () =>
745
+ throttle(
746
+ () => {
747
+ if (channel) {
748
+ setRead(channel);
749
+ }
750
+ },
751
+ stateUpdateThrottleInterval,
752
+ throttleOptions,
753
+ ),
754
+ [channel, stateUpdateThrottleInterval, setRead],
755
+ );
756
+
757
+ const copyMessagesStateFromChannelThrottled = useMemo(
758
+ () =>
759
+ throttle(
760
+ () => {
761
+ if (channel) {
762
+ copyMessagesStateFromChannel(channel);
763
+ }
764
+ },
765
+ newMessageStateUpdateThrottleInterval,
766
+ throttleOptions,
767
+ ),
768
+ [channel, newMessageStateUpdateThrottleInterval, copyMessagesStateFromChannel],
769
+ );
741
770
 
742
771
  const copyChannelState = useMemo(
743
772
  () =>
@@ -748,13 +777,13 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
748
777
  copyMessagesStateFromChannel(channel);
749
778
  }
750
779
  },
751
- copyChannelStateThrottlingTime,
780
+ stateUpdateThrottleInterval,
752
781
  throttleOptions,
753
782
  ),
754
- [channel, copyChannelStateThrottlingTime, copyMessagesStateFromChannel, copyStateFromChannel],
783
+ [stateUpdateThrottleInterval, channel, copyStateFromChannel, copyMessagesStateFromChannel],
755
784
  );
756
785
 
757
- const handleEvent: EventHandler = (event) => {
786
+ const handleEvent: EventHandler = useStableCallback((event) => {
758
787
  if (shouldSyncChannel) {
759
788
  /**
760
789
  * Ignore user.watching.start and user.watching.stop as we should not copy the entire state when
@@ -770,9 +799,11 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
770
799
  }
771
800
 
772
801
  // If the event is typing.start or typing.stop, set the typing state
773
- const isTypingEvent = event.type === 'typing.start' || event.type === 'typing.stop';
774
- if (isTypingEvent) {
775
- setTyping(channel);
802
+ if (event.type === 'typing.start' || event.type === 'typing.stop') {
803
+ if (event.user?.id !== client.userID) {
804
+ setTyping(channel);
805
+ }
806
+ return;
776
807
  } else {
777
808
  if (thread?.id) {
778
809
  const updatedThreadMessages =
@@ -806,17 +837,35 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
806
837
 
807
838
  // only update channel state if the events are not the previously subscribed useEffect's subscription events
808
839
  if (channel && channel.initialized) {
840
+ // we skip the new message events if we've already done an optimistic update for the new message
841
+ if (event.type === 'message.new' || event.type === 'notification.message_new') {
842
+ const messageId = event.message?.id ?? '';
843
+ if (
844
+ event.user?.id !== client.userID ||
845
+ !optimisticallyUpdatedNewMessages.has(messageId)
846
+ ) {
847
+ copyMessagesStateFromChannelThrottled();
848
+ }
849
+ optimisticallyUpdatedNewMessages.delete(messageId);
850
+ return;
851
+ }
852
+
853
+ if (event.type === 'message.read' || event.type === 'notification.mark_read') {
854
+ setReadThrottled();
855
+ return;
856
+ }
857
+
809
858
  copyChannelState();
810
859
  }
811
860
  }
812
- };
861
+ });
813
862
 
814
863
  useEffect(() => {
815
864
  let listener: ReturnType<typeof channel.on>;
816
865
  const initChannel = async () => {
817
866
  setLastRead(new Date());
818
867
  const unreadCount = channel.countUnread();
819
- if (!channel || !shouldSyncChannel || channel.offlineMode) {
868
+ if (!channel || !shouldSyncChannel) {
820
869
  return;
821
870
  }
822
871
  let errored = false;
@@ -887,20 +936,6 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
887
936
  return unsubscribe;
888
937
  }, [channel?.cid, client]);
889
938
 
890
- /**
891
- * Subscription to the Notification mark_read event.
892
- */
893
- useEffect(() => {
894
- const handleEvent: EventHandler = (event) => {
895
- if (channel.cid === event.cid) {
896
- setRead(channel);
897
- }
898
- };
899
-
900
- const { unsubscribe } = client.on('notification.mark_read', handleEvent);
901
- return unsubscribe;
902
- }, [channel, client, setRead]);
903
-
904
939
  const threadPropsExists = !!threadProps;
905
940
 
906
941
  useEffect(() => {
@@ -931,7 +966,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
931
966
  /**
932
967
  * CHANNEL METHODS
933
968
  */
934
- const markRead: ChannelContextValue['markRead'] = throttle(
969
+ const markReadInternal: ChannelContextValue['markRead'] = throttle(
935
970
  async (options?: MarkReadFunctionOptions) => {
936
971
  const { updateChannelUnreadState = true } = options ?? {};
937
972
  if (!channel || channel?.disconnected || !clientChannelConfig?.read_events) {
@@ -949,6 +984,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
949
984
  last_read_message_id: response?.event.last_read_message_id,
950
985
  unread_messages: 0,
951
986
  });
987
+ setLastRead(new Date());
952
988
  }
953
989
  } catch (err) {
954
990
  console.log('Error marking channel as read:', err);
@@ -959,7 +995,9 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
959
995
  throttleOptions,
960
996
  );
961
997
 
962
- const reloadThread = async () => {
998
+ const markRead = useStableCallback(markReadInternal);
999
+
1000
+ const reloadThread = useStableCallback(async () => {
963
1001
  if (!channel || !thread?.id) {
964
1002
  return;
965
1003
  }
@@ -992,9 +1030,9 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
992
1030
  setThreadLoadingMore(false);
993
1031
  throw err;
994
1032
  }
995
- };
1033
+ });
996
1034
 
997
- const resyncChannel = async () => {
1035
+ const resyncChannel = useStableCallback(async () => {
998
1036
  if (!channel || syncingChannelRef.current) {
999
1037
  return;
1000
1038
  }
@@ -1009,7 +1047,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1009
1047
  });
1010
1048
  }
1011
1049
 
1012
- const parseMessage = (message: FormatMessageResponse) =>
1050
+ const parseMessage = (message: LocalMessage) =>
1013
1051
  ({
1014
1052
  ...message,
1015
1053
  created_at: message.created_at.toString(),
@@ -1050,7 +1088,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1050
1088
  }
1051
1089
 
1052
1090
  syncingChannelRef.current = false;
1053
- };
1091
+ });
1054
1092
 
1055
1093
  // resync channel is added to ref so that it can be used in useEffect without adding it as a dependency
1056
1094
  const resyncChannelRef = useRef(resyncChannel);
@@ -1101,136 +1139,142 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1101
1139
  */
1102
1140
  const clientChannelConfig = getChannelConfigSafely();
1103
1141
 
1104
- const reloadChannel = async () => {
1142
+ const reloadChannel = useStableCallback(async () => {
1105
1143
  try {
1106
1144
  await loadLatestMessages();
1107
1145
  } catch (err) {
1108
1146
  console.warn('Reloading channel failed with error:', err);
1109
1147
  }
1110
- };
1148
+ });
1111
1149
 
1112
- const loadChannelAroundMessage: ChannelContextValue['loadChannelAroundMessage'] = async ({
1113
- messageId: messageIdToLoadAround,
1114
- }): Promise<void> => {
1115
- if (!messageIdToLoadAround) {
1116
- return;
1117
- }
1118
- try {
1119
- if (thread) {
1120
- setThreadLoadingMore(true);
1121
- try {
1122
- await channel.state.loadMessageIntoState(messageIdToLoadAround, thread.id);
1123
- setThreadLoadingMore(false);
1124
- setThreadMessages(channel.state.threads[thread.id]);
1125
- if (setTargetedMessage) {
1126
- setTargetedMessage(messageIdToLoadAround);
1127
- }
1128
- } catch (err) {
1129
- if (err instanceof Error) {
1130
- setError(err);
1131
- } else {
1132
- setError(true);
1150
+ const loadChannelAroundMessage: ChannelContextValue['loadChannelAroundMessage'] =
1151
+ useStableCallback(async ({ messageId: messageIdToLoadAround }): Promise<void> => {
1152
+ if (!messageIdToLoadAround) {
1153
+ return;
1154
+ }
1155
+ try {
1156
+ if (thread) {
1157
+ setThreadLoadingMore(true);
1158
+ try {
1159
+ await channel.state.loadMessageIntoState(messageIdToLoadAround, thread.id);
1160
+ setThreadLoadingMore(false);
1161
+ setThreadMessages(channel.state.threads[thread.id]);
1162
+ if (setTargetedMessage) {
1163
+ setTargetedMessage(messageIdToLoadAround);
1164
+ }
1165
+ } catch (err) {
1166
+ if (err instanceof Error) {
1167
+ setError(err);
1168
+ } else {
1169
+ setError(true);
1170
+ }
1171
+ setThreadLoadingMore(false);
1133
1172
  }
1134
- setThreadLoadingMore(false);
1173
+ } else {
1174
+ await loadChannelAroundMessageFn({
1175
+ messageId: messageIdToLoadAround,
1176
+ setTargetedMessage,
1177
+ });
1135
1178
  }
1136
- } else {
1137
- await loadChannelAroundMessageFn({
1138
- messageId: messageIdToLoadAround,
1139
- setTargetedMessage,
1140
- });
1179
+ } catch (err) {
1180
+ console.warn('Loading channel around message failed with error:', err);
1141
1181
  }
1142
- } catch (err) {
1143
- console.warn('Loading channel around message failed with error:', err);
1144
- }
1145
- };
1182
+ });
1146
1183
 
1147
1184
  /**
1148
1185
  * MESSAGE METHODS
1149
1186
  */
1150
- const updateMessage: MessagesContextValue['updateMessage'] = (
1151
- updatedMessage,
1152
- extraState = {},
1153
- ) => {
1154
- if (!channel) {
1155
- return;
1156
- }
1187
+ const updateMessage: MessagesContextValue['updateMessage'] = useStableCallback(
1188
+ (updatedMessage, extraState = {}, throttled = false) => {
1189
+ if (!channel) {
1190
+ return;
1191
+ }
1157
1192
 
1158
- channel.state.addMessageSorted(updatedMessage, true);
1159
- copyMessagesStateFromChannel(channel);
1193
+ channel.state.addMessageSorted(updatedMessage, true);
1194
+ if (throttled) {
1195
+ copyMessagesStateFromChannelThrottled();
1196
+ } else {
1197
+ copyMessagesStateFromChannel(channel);
1198
+ }
1160
1199
 
1161
- if (thread && updatedMessage.parent_id) {
1162
- extraState.threadMessages = channel.state.threads[updatedMessage.parent_id] || [];
1163
- setThreadMessages(extraState.threadMessages);
1164
- }
1165
- };
1200
+ if (thread && updatedMessage.parent_id) {
1201
+ extraState.threadMessages = channel.state.threads[updatedMessage.parent_id] || [];
1202
+ setThreadMessages(extraState.threadMessages);
1203
+ }
1204
+ },
1205
+ );
1166
1206
 
1167
- const replaceMessage = (oldMessage: MessageResponse, newMessage: MessageResponse) => {
1168
- if (channel) {
1169
- channel.state.removeMessage(oldMessage);
1170
- channel.state.addMessageSorted(newMessage, true);
1171
- copyMessagesStateFromChannel(channel);
1207
+ const replaceMessage = useStableCallback(
1208
+ (oldMessage: MessageResponse, newMessage: MessageResponse) => {
1209
+ if (channel) {
1210
+ channel.state.removeMessage(oldMessage);
1211
+ channel.state.addMessageSorted(newMessage, true);
1212
+ copyMessagesStateFromChannel(channel);
1172
1213
 
1173
- if (thread && newMessage.parent_id) {
1174
- const threadMessages = channel.state.threads[newMessage.parent_id] || [];
1175
- setThreadMessages(threadMessages);
1214
+ if (thread && newMessage.parent_id) {
1215
+ const threadMessages = channel.state.threads[newMessage.parent_id] || [];
1216
+ setThreadMessages(threadMessages);
1217
+ }
1176
1218
  }
1177
- }
1178
- };
1219
+ },
1220
+ );
1179
1221
 
1180
- const createMessagePreview = ({
1181
- attachments,
1182
- mentioned_users,
1183
- parent_id,
1184
- poll_id,
1185
- text,
1186
- ...extraFields
1187
- }: Partial<StreamMessage>) => {
1188
- // Exclude following properties from message.user within message preview,
1189
- // since they could be long arrays and have no meaning as sender of message.
1190
- // Storing such large value within user's table may cause sqlite queries to crash.
1191
- // @ts-ignore
1192
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1193
- const { channel_mutes, devices, mutes, ...messageUser } = client.user;
1194
-
1195
- const preview = {
1196
- __html: text,
1222
+ const createMessagePreview = useStableCallback(
1223
+ ({
1197
1224
  attachments,
1198
- created_at: new Date(),
1199
- html: text,
1200
- id: `${client.userID}-${generateRandomId()}`,
1201
- mentioned_users:
1202
- mentioned_users?.map((userId) => ({
1203
- id: userId,
1204
- })) || [],
1225
+ mentioned_users,
1205
1226
  parent_id,
1206
1227
  poll_id,
1207
- reactions: [],
1208
- status: MessageStatusTypes.SENDING,
1209
1228
  text,
1210
- type: 'regular',
1211
- user: {
1212
- ...messageUser,
1213
- id: client.userID,
1214
- },
1215
- ...extraFields,
1216
- } as unknown as MessageResponse;
1229
+ ...extraFields
1230
+ }: Partial<StreamMessage>) => {
1231
+ // Exclude following properties from message.user within message preview,
1232
+ // since they could be long arrays and have no meaning as sender of message.
1233
+ // Storing such large value within user's table may cause sqlite queries to crash.
1234
+ // @ts-ignore
1235
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1236
+ const { channel_mutes, devices, mutes, ...messageUser } = client.user;
1237
+
1238
+ const preview = {
1239
+ __html: text,
1240
+ attachments,
1241
+ created_at: new Date(),
1242
+ html: text,
1243
+ id: `${client.userID}-${generateRandomId()}`,
1244
+ mentioned_users:
1245
+ mentioned_users?.map((userId) => ({
1246
+ id: userId,
1247
+ })) || [],
1248
+ parent_id,
1249
+ poll_id,
1250
+ reactions: [],
1251
+ status: MessageStatusTypes.SENDING,
1252
+ text,
1253
+ type: 'regular',
1254
+ user: {
1255
+ ...messageUser,
1256
+ id: client.userID,
1257
+ },
1258
+ ...extraFields,
1259
+ } as unknown as MessageResponse;
1217
1260
 
1218
- /**
1219
- * This is added to the message for local rendering prior to the message
1220
- * being returned from the backend, it is removed when the message is sent
1221
- * as quoted_message is a reserved field.
1222
- */
1223
- if (preview.quoted_message_id) {
1224
- const quotedMessage = channelMessagesState.messages?.find(
1225
- (message) => message.id === preview.quoted_message_id,
1226
- );
1261
+ /**
1262
+ * This is added to the message for local rendering prior to the message
1263
+ * being returned from the backend, it is removed when the message is sent
1264
+ * as quoted_message is a reserved field.
1265
+ */
1266
+ if (preview.quoted_message_id) {
1267
+ const quotedMessage = channelMessagesState.messages?.find(
1268
+ (message) => message.id === preview.quoted_message_id,
1269
+ );
1227
1270
 
1228
- preview.quoted_message = quotedMessage as MessageResponse['quoted_message'];
1229
- }
1230
- return preview;
1231
- };
1271
+ preview.quoted_message = quotedMessage as MessageResponse['quoted_message'];
1272
+ }
1273
+ return preview;
1274
+ },
1275
+ );
1232
1276
 
1233
- const uploadPendingAttachments = async (message: MessageResponse) => {
1277
+ const uploadPendingAttachments = useStableCallback(async (message: MessageResponse) => {
1234
1278
  const updatedMessage = { ...message };
1235
1279
  if (updatedMessage.attachments?.length) {
1236
1280
  for (let i = 0; i < updatedMessage.attachments?.length; i++) {
@@ -1283,7 +1327,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1283
1327
  }
1284
1328
  const response = doDocUploadRequest
1285
1329
  ? await doDocUploadRequest(file, channel)
1286
- : await channel.sendFile(file.uri, file.name, file.mimeType);
1330
+ : await channel.sendFile(file.uri, file.name, file.type);
1287
1331
  attachment.asset_url = response.file;
1288
1332
  if (response.thumb_url) {
1289
1333
  attachment.thumb_url = response.thumb_url;
@@ -1298,174 +1342,185 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1298
1342
  }
1299
1343
 
1300
1344
  return updatedMessage;
1301
- };
1302
-
1303
- const sendMessageRequest = async (message: MessageResponse, retrying?: boolean) => {
1304
- try {
1305
- const updatedMessage = await uploadPendingAttachments(message);
1306
- const extraFields = omit(updatedMessage, [
1307
- '__html',
1308
- 'attachments',
1309
- 'created_at',
1310
- 'deleted_at',
1311
- 'html',
1312
- 'id',
1313
- 'latest_reactions',
1314
- 'mentioned_users',
1315
- 'own_reactions',
1316
- 'parent_id',
1317
- 'quoted_message',
1318
- 'reaction_counts',
1319
- 'reaction_groups',
1320
- 'reactions',
1321
- 'status',
1322
- 'text',
1323
- 'type',
1324
- 'updated_at',
1325
- 'user',
1326
- ]);
1327
- const { attachments, id, mentioned_users, parent_id, text } = updatedMessage;
1328
- if (!channel.id) {
1329
- return;
1330
- }
1345
+ });
1331
1346
 
1332
- const mentionedUserIds = mentioned_users?.map((user) => user.id) || [];
1347
+ const sendMessageRequest = useStableCallback(
1348
+ async (message: MessageResponse, retrying?: boolean) => {
1349
+ try {
1350
+ const updatedMessage = await uploadPendingAttachments(message);
1351
+ const extraFields = omit(updatedMessage, [
1352
+ '__html',
1353
+ 'attachments',
1354
+ 'created_at',
1355
+ 'deleted_at',
1356
+ 'html',
1357
+ 'id',
1358
+ 'latest_reactions',
1359
+ 'mentioned_users',
1360
+ 'own_reactions',
1361
+ 'parent_id',
1362
+ 'quoted_message',
1363
+ 'reaction_counts',
1364
+ 'reaction_groups',
1365
+ 'reactions',
1366
+ 'status',
1367
+ 'text',
1368
+ 'type',
1369
+ 'updated_at',
1370
+ 'user',
1371
+ ]);
1372
+ const { attachments, id, mentioned_users, parent_id, text } = updatedMessage;
1373
+ if (!channel.id) {
1374
+ return;
1375
+ }
1333
1376
 
1334
- const messageData = {
1335
- attachments,
1336
- id,
1337
- mentioned_users: mentionedUserIds,
1338
- parent_id,
1339
- text: patchMessageTextCommand(text ?? '', mentionedUserIds),
1340
- ...extraFields,
1341
- } as StreamMessage;
1377
+ const mentionedUserIds = mentioned_users?.map((user) => user.id) || [];
1378
+
1379
+ const messageData = {
1380
+ attachments,
1381
+ id,
1382
+ mentioned_users: mentionedUserIds,
1383
+ parent_id,
1384
+ text: patchMessageTextCommand(text ?? '', mentionedUserIds),
1385
+ ...extraFields,
1386
+ } as StreamMessage;
1387
+
1388
+ let messageResponse = {} as SendMessageAPIResponse;
1389
+ if (doSendMessageRequest) {
1390
+ messageResponse = await doSendMessageRequest(channel?.cid || '', messageData);
1391
+ } else if (channel) {
1392
+ messageResponse = await channel.sendMessage(messageData);
1393
+ }
1342
1394
 
1343
- let messageResponse = {} as SendMessageAPIResponse;
1344
- if (doSendMessageRequest) {
1345
- messageResponse = await doSendMessageRequest(channel?.cid || '', messageData);
1346
- } else if (channel) {
1347
- messageResponse = await channel.sendMessage(messageData);
1348
- }
1395
+ if (messageResponse.message) {
1396
+ messageResponse.message.status = MessageStatusTypes.RECEIVED;
1349
1397
 
1350
- if (messageResponse.message) {
1351
- messageResponse.message.status = MessageStatusTypes.RECEIVED;
1398
+ if (enableOfflineSupport) {
1399
+ await dbApi.updateMessage({
1400
+ message: { ...messageResponse.message, cid: channel.cid },
1401
+ });
1402
+ }
1403
+ if (retrying) {
1404
+ replaceMessage(message, messageResponse.message);
1405
+ } else {
1406
+ updateMessage(messageResponse.message, {}, true);
1407
+ }
1408
+ }
1409
+ } catch (err) {
1410
+ console.log(err);
1411
+ message.status = MessageStatusTypes.FAILED;
1412
+ const updatedMessage = { ...message, cid: channel.cid };
1413
+ updateMessage(updatedMessage);
1414
+ threadInstance?.upsertReplyLocally?.({ message: updatedMessage });
1415
+ optimisticallyUpdatedNewMessages.delete(message.id);
1352
1416
 
1353
1417
  if (enableOfflineSupport) {
1354
1418
  await dbApi.updateMessage({
1355
- message: { ...messageResponse.message, cid: channel.cid },
1419
+ message: { ...message, cid: channel.cid },
1356
1420
  });
1357
1421
  }
1358
- if (retrying) {
1359
- replaceMessage(message, messageResponse.message);
1360
- } else {
1361
- updateMessage(messageResponse.message);
1362
- }
1363
-
1364
- threadInstance?.upsertReplyLocally?.({ message: messageResponse.message });
1365
1422
  }
1366
- } catch (err) {
1367
- console.log(err);
1368
- message.status = MessageStatusTypes.FAILED;
1369
- const updatedMessage = { ...message, cid: channel.cid };
1370
- updateMessage(updatedMessage);
1371
- threadInstance?.upsertReplyLocally?.({ message: updatedMessage });
1423
+ },
1424
+ );
1372
1425
 
1373
- if (enableOfflineSupport) {
1374
- await dbApi.updateMessage({
1375
- message: { ...message, cid: channel.cid },
1376
- });
1426
+ const sendMessage: InputMessageInputContextValue['sendMessage'] = useStableCallback(
1427
+ async (message) => {
1428
+ if (channel?.state?.filterErrorMessages) {
1429
+ channel.state.filterErrorMessages();
1377
1430
  }
1378
- }
1379
- };
1380
-
1381
- const sendMessage: InputMessageInputContextValue['sendMessage'] = async (message) => {
1382
- if (channel?.state?.filterErrorMessages) {
1383
- channel.state.filterErrorMessages();
1384
- }
1385
1431
 
1386
- const messagePreview = createMessagePreview({
1387
- ...message,
1388
- attachments: message.attachments || [],
1389
- });
1390
-
1391
- updateMessage(messagePreview, {
1392
- commands: [],
1393
- messageInput: '',
1394
- });
1395
- threadInstance?.upsertReplyLocally?.({ message: messagePreview });
1432
+ const messagePreview = createMessagePreview({
1433
+ ...message,
1434
+ attachments: message.attachments || [],
1435
+ });
1396
1436
 
1397
- if (enableOfflineSupport) {
1398
- // While sending a message, we add the message to local db with failed status, so that
1399
- // if app gets closed before message gets sent and next time user opens the app
1400
- // then user can see that message in failed state and can retry.
1401
- // If succesfull, it will be updated with received status.
1402
- await dbApi.upsertMessages({
1403
- messages: [{ ...messagePreview, cid: channel.cid, status: MessageStatusTypes.FAILED }],
1437
+ updateMessage(messagePreview, {
1438
+ commands: [],
1439
+ messageInput: '',
1404
1440
  });
1405
- }
1441
+ threadInstance?.upsertReplyLocally?.({ message: messagePreview });
1442
+ optimisticallyUpdatedNewMessages.add(messagePreview.id);
1406
1443
 
1407
- await sendMessageRequest(messagePreview);
1408
- };
1444
+ if (enableOfflineSupport) {
1445
+ // While sending a message, we add the message to local db with failed status, so that
1446
+ // if app gets closed before message gets sent and next time user opens the app
1447
+ // then user can see that message in failed state and can retry.
1448
+ // If succesfull, it will be updated with received status.
1449
+ await dbApi.upsertMessages({
1450
+ messages: [{ ...messagePreview, cid: channel.cid, status: MessageStatusTypes.FAILED }],
1451
+ });
1452
+ }
1409
1453
 
1410
- const retrySendMessage: MessagesContextValue['retrySendMessage'] = async (message) => {
1411
- const statusPendingMessage = {
1412
- ...message,
1413
- status: MessageStatusTypes.SENDING,
1414
- };
1454
+ await sendMessageRequest(messagePreview);
1455
+ },
1456
+ );
1415
1457
 
1416
- const messageWithoutReservedFields = removeReservedFields(statusPendingMessage);
1458
+ const retrySendMessage: MessagesContextValue['retrySendMessage'] = useStableCallback(
1459
+ async (message) => {
1460
+ const statusPendingMessage = {
1461
+ ...message,
1462
+ status: MessageStatusTypes.SENDING,
1463
+ };
1417
1464
 
1418
- // For bounced messages, we don't need to update the message, instead always send a new message.
1419
- if (!isBouncedMessage(message)) {
1420
- updateMessage(messageWithoutReservedFields as MessageResponse);
1421
- }
1465
+ const messageWithoutReservedFields = removeReservedFields(statusPendingMessage);
1422
1466
 
1423
- await sendMessageRequest(messageWithoutReservedFields as MessageResponse, true);
1424
- };
1467
+ // For bounced messages, we don't need to update the message, instead always send a new message.
1468
+ if (!isBouncedMessage(message)) {
1469
+ updateMessage(messageWithoutReservedFields as MessageResponse);
1470
+ }
1425
1471
 
1426
- const editMessage: InputMessageInputContextValue['editMessage'] = (updatedMessage) =>
1427
- doUpdateMessageRequest
1428
- ? doUpdateMessageRequest(channel?.cid || '', updatedMessage)
1429
- : client.updateMessage(updatedMessage);
1472
+ await sendMessageRequest(messageWithoutReservedFields as MessageResponse, true);
1473
+ },
1474
+ );
1475
+
1476
+ const editMessage: InputMessageInputContextValue['editMessage'] = useStableCallback(
1477
+ (updatedMessage) =>
1478
+ doUpdateMessageRequest
1479
+ ? doUpdateMessageRequest(channel?.cid || '', updatedMessage)
1480
+ : client.updateMessage(updatedMessage),
1481
+ );
1430
1482
 
1431
- const setEditingState: MessagesContextValue['setEditingState'] = (message) => {
1483
+ const setEditingState: MessagesContextValue['setEditingState'] = useStableCallback((message) => {
1432
1484
  clearQuotedMessageState();
1433
1485
  setEditing(message);
1434
- };
1486
+ });
1435
1487
 
1436
- const setQuotedMessageState: MessagesContextValue['setQuotedMessageState'] = (
1437
- messageOrBoolean,
1438
- ) => {
1439
- setQuotedMessage(messageOrBoolean);
1440
- };
1488
+ const setQuotedMessageState: MessagesContextValue['setQuotedMessageState'] = useStableCallback(
1489
+ (messageOrBoolean) => {
1490
+ setQuotedMessage(messageOrBoolean);
1491
+ },
1492
+ );
1441
1493
 
1442
- const clearEditingState: InputMessageInputContextValue['clearEditingState'] = () =>
1443
- setEditing(undefined);
1494
+ const clearEditingState: InputMessageInputContextValue['clearEditingState'] = useStableCallback(
1495
+ () => setEditing(undefined),
1496
+ );
1444
1497
 
1445
- const clearQuotedMessageState: InputMessageInputContextValue['clearQuotedMessageState'] = () =>
1446
- setQuotedMessage(undefined);
1498
+ const clearQuotedMessageState: InputMessageInputContextValue['clearQuotedMessageState'] =
1499
+ useStableCallback(() => setQuotedMessage(undefined));
1447
1500
 
1448
1501
  /**
1449
1502
  * Removes the message from local state
1450
1503
  */
1451
- const removeMessage: MessagesContextValue['removeMessage'] = async (message) => {
1452
- if (channel) {
1453
- channel.state.removeMessage(message);
1454
- copyMessagesStateFromChannel(channel);
1504
+ const removeMessage: MessagesContextValue['removeMessage'] = useStableCallback(
1505
+ async (message) => {
1506
+ if (channel) {
1507
+ channel.state.removeMessage(message);
1508
+ copyMessagesStateFromChannel(channel);
1455
1509
 
1456
- if (thread) {
1457
- setThreadMessages(channel.state.threads[thread.id] || []);
1510
+ if (thread) {
1511
+ setThreadMessages(channel.state.threads[thread.id] || []);
1512
+ }
1458
1513
  }
1459
- }
1460
1514
 
1461
- if (enableOfflineSupport) {
1462
- await dbApi.deleteMessage({
1463
- id: message.id,
1464
- });
1465
- }
1466
- };
1515
+ if (enableOfflineSupport) {
1516
+ await dbApi.deleteMessage({
1517
+ id: message.id,
1518
+ });
1519
+ }
1520
+ },
1521
+ );
1467
1522
 
1468
- const sendReaction = async (type: string, messageId: string) => {
1523
+ const sendReaction = useStableCallback(async (type: string, messageId: string) => {
1469
1524
  if (!channel?.id || !client.user) {
1470
1525
  throw new Error('Channel has not been initialized');
1471
1526
  }
@@ -1506,88 +1561,89 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1506
1561
  if (sendReactionResponse?.message) {
1507
1562
  threadInstance?.upsertReplyLocally?.({ message: sendReactionResponse.message });
1508
1563
  }
1509
- };
1564
+ });
1510
1565
 
1511
- const deleteMessage: MessagesContextValue['deleteMessage'] = async (message) => {
1512
- if (!channel.id) {
1513
- throw new Error('Channel has not been initialized yet');
1514
- }
1566
+ const deleteMessage: MessagesContextValue['deleteMessage'] = useStableCallback(
1567
+ async (message) => {
1568
+ if (!channel.id) {
1569
+ throw new Error('Channel has not been initialized yet');
1570
+ }
1571
+
1572
+ if (!enableOfflineSupport) {
1573
+ if (message.status === MessageStatusTypes.FAILED) {
1574
+ await removeMessage(message);
1575
+ return;
1576
+ }
1577
+ await client.deleteMessage(message.id);
1578
+ return;
1579
+ }
1515
1580
 
1516
- if (!enableOfflineSupport) {
1517
1581
  if (message.status === MessageStatusTypes.FAILED) {
1582
+ await DBSyncManager.dropPendingTasks({ messageId: message.id });
1518
1583
  await removeMessage(message);
1584
+ } else {
1585
+ const updatedMessage = {
1586
+ ...message,
1587
+ cid: channel.cid,
1588
+ deleted_at: new Date(),
1589
+ type: 'deleted' as MessageLabel,
1590
+ };
1591
+ updateMessage(updatedMessage);
1592
+
1593
+ threadInstance?.upsertReplyLocally({ message: updatedMessage });
1594
+
1595
+ const data = await DBSyncManager.queueTask({
1596
+ client,
1597
+ task: {
1598
+ channelId: channel.id,
1599
+ channelType: channel.type,
1600
+ messageId: message.id,
1601
+ payload: [message.id],
1602
+ type: 'delete-message',
1603
+ },
1604
+ });
1605
+
1606
+ if (data?.message) {
1607
+ updateMessage({ ...data.message });
1608
+ }
1609
+ }
1610
+ },
1611
+ );
1612
+
1613
+ const deleteReaction: MessagesContextValue['deleteReaction'] = useStableCallback(
1614
+ async (type: string, messageId: string) => {
1615
+ if (!channel?.id || !client.user) {
1616
+ throw new Error('Channel has not been initialized');
1617
+ }
1618
+
1619
+ const payload: Parameters<ChannelClass['deleteReaction']> = [messageId, type];
1620
+
1621
+ if (!enableOfflineSupport) {
1622
+ await channel.deleteReaction(...payload);
1519
1623
  return;
1520
1624
  }
1521
- await client.deleteMessage(message.id);
1522
- return;
1523
- }
1524
1625
 
1525
- if (message.status === MessageStatusTypes.FAILED) {
1526
- await DBSyncManager.dropPendingTasks({ messageId: message.id });
1527
- await removeMessage(message);
1528
- } else {
1529
- const updatedMessage = {
1530
- ...message,
1531
- cid: channel.cid,
1532
- deleted_at: new Date().toISOString(),
1533
- type: 'deleted' as MessageLabel,
1534
- };
1535
- updateMessage(updatedMessage);
1626
+ removeReactionFromLocalState({
1627
+ channel,
1628
+ messageId,
1629
+ reactionType: type,
1630
+ user: client.user,
1631
+ });
1536
1632
 
1537
- threadInstance?.upsertReplyLocally({ message: updatedMessage });
1633
+ copyMessagesStateFromChannel(channel);
1538
1634
 
1539
- const data = await DBSyncManager.queueTask({
1635
+ await DBSyncManager.queueTask({
1540
1636
  client,
1541
1637
  task: {
1542
1638
  channelId: channel.id,
1543
1639
  channelType: channel.type,
1544
- messageId: message.id,
1545
- payload: [message.id],
1546
- type: 'delete-message',
1640
+ messageId,
1641
+ payload,
1642
+ type: 'delete-reaction',
1547
1643
  },
1548
1644
  });
1549
-
1550
- if (data?.message) {
1551
- updateMessage({ ...data.message });
1552
- }
1553
- }
1554
- };
1555
-
1556
- const deleteReaction: MessagesContextValue['deleteReaction'] = async (
1557
- type: string,
1558
- messageId: string,
1559
- ) => {
1560
- if (!channel?.id || !client.user) {
1561
- throw new Error('Channel has not been initialized');
1562
- }
1563
-
1564
- const payload: Parameters<ChannelClass['deleteReaction']> = [messageId, type];
1565
-
1566
- if (!enableOfflineSupport) {
1567
- await channel.deleteReaction(...payload);
1568
- return;
1569
- }
1570
-
1571
- removeReactionFromLocalState({
1572
- channel,
1573
- messageId,
1574
- reactionType: type,
1575
- user: client.user,
1576
- });
1577
-
1578
- copyMessagesStateFromChannel(channel);
1579
-
1580
- await DBSyncManager.queueTask({
1581
- client,
1582
- task: {
1583
- channelId: channel.id,
1584
- channelType: channel.type,
1585
- messageId,
1586
- payload,
1587
- type: 'delete-reaction',
1588
- },
1589
- });
1590
- };
1645
+ },
1646
+ );
1591
1647
 
1592
1648
  /**
1593
1649
  * THREAD METHODS
@@ -1626,7 +1682,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1626
1682
  ),
1627
1683
  ).current;
1628
1684
 
1629
- const loadMoreThread: ThreadContextValue['loadMoreThread'] = async () => {
1685
+ const loadMoreThread: ThreadContextValue['loadMoreThread'] = useStableCallback(async () => {
1630
1686
  if (threadLoadingMore || !thread?.id) {
1631
1687
  return;
1632
1688
  }
@@ -1665,7 +1721,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1665
1721
  setThreadLoadingMore(false);
1666
1722
  throw err;
1667
1723
  }
1668
- };
1724
+ });
1669
1725
 
1670
1726
  const ownCapabilitiesContext = useCreateOwnCapabilitiesContext({
1671
1727
  channel,
@@ -1717,8 +1773,11 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1717
1773
  // but it is definitely not trivial, especially considering it depends on other inline functions that
1718
1774
  // are not wrapped in a useCallback() themselves hence creating a huge cascading change. Can be removed
1719
1775
  // once our memoization issues are fixed in most places in the app or we move to a reactive state store.
1720
- const sendMessageRef = useRef<InputMessageInputContextValue['sendMessage']>(sendMessage);
1721
- sendMessageRef.current = sendMessage;
1776
+ // const sendMessageRef = useRef<InputMessageInputContextValue['sendMessage']>(sendMessage);
1777
+ // sendMessageRef.current = sendMessage;
1778
+ // const sendMessageStable = useCallback<InputMessageInputContextValue['sendMessage']>((...args) => {
1779
+ // return sendMessageRef.current(...args);
1780
+ // }, []);
1722
1781
 
1723
1782
  const inputMessageInputContext = useCreateInputMessageInputContext({
1724
1783
  additionalTextInputProps,
@@ -1751,7 +1810,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1751
1810
  FileUploadPreview,
1752
1811
  handleAttachButtonPress,
1753
1812
  hasCameraPicker,
1754
- hasCommands,
1813
+ hasCommands: hasCommands ?? (getChannelConfigSafely()?.commands ?? []).length > 0,
1755
1814
  hasFilePicker,
1756
1815
  hasImagePicker,
1757
1816
  ImageUploadPreview,
@@ -1772,7 +1831,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1772
1831
  quotedMessage,
1773
1832
  SendButton,
1774
1833
  sendImageAsync,
1775
- sendMessage: (...args) => sendMessageRef.current(...args),
1834
+ sendMessage,
1776
1835
  SendMessageDisallowedIndicator,
1777
1836
  setInputRef,
1778
1837
  setQuotedMessageState,
@@ -1840,6 +1899,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1840
1899
  hasCreatePoll === undefined ? pollCreationEnabled : hasCreatePoll && pollCreationEnabled,
1841
1900
  ImageLoadingFailedIndicator,
1842
1901
  ImageLoadingIndicator,
1902
+ ImageReloadIndicator,
1843
1903
  initialScrollToFirstUnreadMessage: !messageId && initialScrollToFirstUnreadMessage, // when messageId is set, we scroll to the messageId instead of first unread
1844
1904
  InlineDateSeparator,
1845
1905
  InlineUnreadIndicator,
@@ -1905,11 +1965,13 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1905
1965
  VideoThumbnail,
1906
1966
  });
1907
1967
 
1908
- const suggestionsContext = {
1909
- AutoCompleteSuggestionHeader,
1910
- AutoCompleteSuggestionItem,
1911
- AutoCompleteSuggestionList,
1912
- };
1968
+ const suggestionsContext = useMemo(() => {
1969
+ return {
1970
+ AutoCompleteSuggestionHeader,
1971
+ AutoCompleteSuggestionItem,
1972
+ AutoCompleteSuggestionList,
1973
+ };
1974
+ }, [AutoCompleteSuggestionHeader, AutoCompleteSuggestionItem, AutoCompleteSuggestionList]);
1913
1975
 
1914
1976
  const threadContext = useCreateThreadContext({
1915
1977
  allowThreadMessagesInChannel,
@@ -1976,7 +2038,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1976
2038
 
1977
2039
  export type ChannelProps = Partial<Omit<ChannelPropsWithContext, 'channel' | 'thread'>> &
1978
2040
  Pick<ChannelPropsWithContext, 'channel'> & {
1979
- thread?: MessageType | ThreadType | null;
2041
+ thread?: LocalMessage | ThreadType | null;
1980
2042
  };
1981
2043
 
1982
2044
  /**
@@ -1995,7 +2057,7 @@ export const Channel = (props: PropsWithChildren<ChannelProps>) => {
1995
2057
  const threadInstance = (threadFromProps as ThreadType)?.threadInstance as Thread;
1996
2058
  const threadMessage = (
1997
2059
  threadInstance ? (threadFromProps as ThreadType).thread : threadFromProps
1998
- ) as MessageType;
2060
+ ) as LocalMessage;
1999
2061
 
2000
2062
  const thread: ThreadType = {
2001
2063
  thread: threadMessage,