stream-chat-react-native-core 10.0.0-rc.2 → 10.0.0-rc.3

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 (500) hide show
  1. package/lib/commonjs/ai-components/README.md +50 -2
  2. package/lib/commonjs/ai-components/index.js +24 -0
  3. package/lib/commonjs/ai-components/index.js.map +1 -1
  4. package/lib/commonjs/ai-components/message-composer/ComposerView.js +4 -2
  5. package/lib/commonjs/ai-components/message-composer/ComposerView.js.map +1 -1
  6. package/lib/commonjs/ai-components/message-composer/SpeechToTextButton.js +4 -1
  7. package/lib/commonjs/ai-components/message-composer/SpeechToTextButton.js.map +1 -1
  8. package/lib/commonjs/ai-components/native/AIDictation.js +6 -0
  9. package/lib/commonjs/ai-components/native/AIDictation.js.map +1 -1
  10. package/lib/commonjs/ai-components/store/dictation/store.js +4 -2
  11. package/lib/commonjs/ai-components/store/dictation/store.js.map +1 -1
  12. package/lib/commonjs/ai-components/store/dictation/useDictationState.js +4 -2
  13. package/lib/commonjs/ai-components/store/dictation/useDictationState.js.map +1 -1
  14. package/lib/commonjs/ai-components/store/index.js +22 -0
  15. package/lib/commonjs/ai-components/store/index.js.map +1 -1
  16. package/lib/commonjs/ai-components/theme.js +3 -0
  17. package/lib/commonjs/ai-components/theme.js.map +1 -1
  18. package/lib/commonjs/ai-components/transcription/useDictation.js +7 -3
  19. package/lib/commonjs/ai-components/transcription/useDictation.js.map +1 -1
  20. package/lib/commonjs/ai-components/transcription/useDictationAudioLevel.js +32 -0
  21. package/lib/commonjs/ai-components/transcription/useDictationAudioLevel.js.map +1 -0
  22. package/lib/commonjs/ai-components/voice-visualization/VoiceGlow.js +79 -0
  23. package/lib/commonjs/ai-components/voice-visualization/VoiceGlow.js.map +1 -0
  24. package/lib/commonjs/ai-components/voice-visualization/VoiceVisualizationView.js +147 -0
  25. package/lib/commonjs/ai-components/voice-visualization/VoiceVisualizationView.js.map +1 -0
  26. package/lib/commonjs/ai-components/voice-visualization/VoiceWave.js +58 -0
  27. package/lib/commonjs/ai-components/voice-visualization/VoiceWave.js.map +1 -0
  28. package/lib/commonjs/ai-components/voice-visualization/bounce.js +60 -0
  29. package/lib/commonjs/ai-components/voice-visualization/bounce.js.map +1 -0
  30. package/lib/commonjs/ai-components/voice-visualization/driver.js +208 -0
  31. package/lib/commonjs/ai-components/voice-visualization/driver.js.map +1 -0
  32. package/lib/commonjs/ai-components/voice-visualization/glowShader.js +218 -0
  33. package/lib/commonjs/ai-components/voice-visualization/glowShader.js.map +1 -0
  34. package/lib/commonjs/ai-components/voice-visualization/index.js +18 -0
  35. package/lib/commonjs/ai-components/voice-visualization/index.js.map +1 -0
  36. package/lib/commonjs/ai-components/voice-visualization/math.js +70 -0
  37. package/lib/commonjs/ai-components/voice-visualization/math.js.map +1 -0
  38. package/lib/commonjs/ai-components/voice-visualization/palette.js +47 -0
  39. package/lib/commonjs/ai-components/voice-visualization/palette.js.map +1 -0
  40. package/lib/commonjs/ai-components/voice-visualization/simplex.js +77 -0
  41. package/lib/commonjs/ai-components/voice-visualization/simplex.js.map +1 -0
  42. package/lib/commonjs/ai-components/voice-visualization/styles.js +33 -0
  43. package/lib/commonjs/ai-components/voice-visualization/styles.js.map +1 -0
  44. package/lib/commonjs/ai-components/voice-visualization/types.js +4 -0
  45. package/lib/commonjs/ai-components/voice-visualization/types.js.map +1 -0
  46. package/lib/commonjs/ai-components/voice-visualization/useReduceMotion.js +28 -0
  47. package/lib/commonjs/ai-components/voice-visualization/useReduceMotion.js.map +1 -0
  48. package/lib/commonjs/ai-components/voice-visualization/useVoiceVisualization.js +129 -0
  49. package/lib/commonjs/ai-components/voice-visualization/useVoiceVisualization.js.map +1 -0
  50. package/lib/commonjs/ai-components/voice-visualization/waveGeometry.js +124 -0
  51. package/lib/commonjs/ai-components/voice-visualization/waveGeometry.js.map +1 -0
  52. package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js +2 -1
  53. package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
  54. package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberItem.js +1 -1
  55. package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberItem.js.map +1 -1
  56. package/lib/commonjs/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js +1 -1
  57. package/lib/commonjs/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js.map +1 -1
  58. package/lib/commonjs/components/ChannelList/ChannelListView.js +0 -2
  59. package/lib/commonjs/components/ChannelList/ChannelListView.js.map +1 -1
  60. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js +1 -1
  61. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  62. package/lib/commonjs/components/ChannelPreview/ChannelDetailsBottomSheet.js +1 -1
  63. package/lib/commonjs/components/ChannelPreview/ChannelDetailsBottomSheet.js.map +1 -1
  64. package/lib/commonjs/components/ChannelPreview/ChannelLastMessagePreview.js +1 -0
  65. package/lib/commonjs/components/ChannelPreview/ChannelLastMessagePreview.js.map +1 -1
  66. package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +2 -0
  67. package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
  68. package/lib/commonjs/components/ChannelPreview/ChannelPreviewTitle.js +1 -0
  69. package/lib/commonjs/components/ChannelPreview/ChannelPreviewTitle.js.map +1 -1
  70. package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js +1 -1
  71. package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
  72. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js +5 -5
  73. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
  74. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +1 -1
  75. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
  76. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js +1 -1
  77. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js.map +1 -1
  78. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +4 -1
  79. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
  80. package/lib/commonjs/components/Chat/hooks/useConnectionLifecycle.js +6 -2
  81. package/lib/commonjs/components/Chat/hooks/useConnectionLifecycle.js.map +1 -1
  82. package/lib/commonjs/components/Indicators/LoadingErrorIndicator.js +5 -0
  83. package/lib/commonjs/components/Indicators/LoadingErrorIndicator.js.map +1 -1
  84. package/lib/commonjs/components/Message/Message.js +1 -1
  85. package/lib/commonjs/components/Message/Message.js.map +1 -1
  86. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js +4 -0
  87. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  88. package/lib/commonjs/components/Message/MessageItemView/MessageWrapper.js +7 -4
  89. package/lib/commonjs/components/Message/MessageItemView/MessageWrapper.js.map +1 -1
  90. package/lib/commonjs/components/Message/hooks/useCreateMessageActionHandlers.js +3 -3
  91. package/lib/commonjs/components/Message/hooks/useCreateMessageActionHandlers.js.map +1 -1
  92. package/lib/commonjs/components/Message/hooks/useProcessReactions.js +2 -2
  93. package/lib/commonjs/components/Message/hooks/useProcessReactions.js.map +1 -1
  94. package/lib/commonjs/components/Message/hooks/useUserMuteActive.js +1 -1
  95. package/lib/commonjs/components/Message/hooks/useUserMuteActive.js.map +1 -1
  96. package/lib/commonjs/components/MessageInput/MessageComposer.js +2 -1
  97. package/lib/commonjs/components/MessageInput/MessageComposer.js.map +1 -1
  98. package/lib/commonjs/components/MessageList/MessageFlashList.js +2 -2
  99. package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
  100. package/lib/commonjs/components/MessageList/MessageList.js +2 -2
  101. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  102. package/lib/commonjs/components/MessageList/hooks/useOwnUnreadCount.js +1 -1
  103. package/lib/commonjs/components/MessageList/hooks/useOwnUnreadCount.js.map +1 -1
  104. package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js +1 -1
  105. package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
  106. package/lib/commonjs/components/MessageMenu/hooks/useMessageOwnReactions.js +2 -2
  107. package/lib/commonjs/components/MessageMenu/hooks/useMessageOwnReactions.js.map +1 -1
  108. package/lib/commonjs/components/Poll/components/PollAnswersList.js +1 -1
  109. package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
  110. package/lib/commonjs/components/Poll/components/PollButtons.js +1 -1
  111. package/lib/commonjs/components/Poll/components/PollButtons.js.map +1 -1
  112. package/lib/commonjs/components/Poll/hook/useIsPollCreatedByCurrentUser.js +1 -1
  113. package/lib/commonjs/components/Poll/hook/useIsPollCreatedByCurrentUser.js.map +1 -1
  114. package/lib/commonjs/components/Thread/Thread.js +1 -1
  115. package/lib/commonjs/components/Thread/Thread.js.map +1 -1
  116. package/lib/commonjs/components/ThreadList/ThreadListItem.js +1 -1
  117. package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -1
  118. package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js +9 -2
  119. package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js.map +1 -1
  120. package/lib/commonjs/components/index.js +24 -0
  121. package/lib/commonjs/components/index.js.map +1 -1
  122. package/lib/commonjs/contexts/componentsContext/defaultComponents.js +0 -2
  123. package/lib/commonjs/contexts/componentsContext/defaultComponents.js.map +1 -1
  124. package/lib/commonjs/contexts/liveLocationManagerContext/LiveLocationManagerContext.js +1 -1
  125. package/lib/commonjs/contexts/liveLocationManagerContext/LiveLocationManagerContext.js.map +1 -1
  126. package/lib/commonjs/hooks/actions/useChannelActionItems.js +1 -1
  127. package/lib/commonjs/hooks/actions/useChannelActionItems.js.map +1 -1
  128. package/lib/commonjs/hooks/actions/useChannelActions.js +2 -2
  129. package/lib/commonjs/hooks/actions/useChannelActions.js.map +1 -1
  130. package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js +1 -1
  131. package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js.map +1 -1
  132. package/lib/commonjs/hooks/index.js +11 -0
  133. package/lib/commonjs/hooks/index.js.map +1 -1
  134. package/lib/commonjs/hooks/messagePreview/useMessageDeliveryStatus.js +6 -6
  135. package/lib/commonjs/hooks/messagePreview/useMessageDeliveryStatus.js.map +1 -1
  136. package/lib/commonjs/hooks/useIsDirectChat.js +1 -1
  137. package/lib/commonjs/hooks/useIsDirectChat.js.map +1 -1
  138. package/lib/commonjs/i18n/runtimeDefaults.js +1 -0
  139. package/lib/commonjs/i18n/runtimeDefaults.js.map +1 -1
  140. package/lib/commonjs/store/apis/getChannelMessages.js +5 -3
  141. package/lib/commonjs/store/apis/getChannelMessages.js.map +1 -1
  142. package/lib/commonjs/store/apis/getChannels.js +6 -3
  143. package/lib/commonjs/store/apis/getChannels.js.map +1 -1
  144. package/lib/commonjs/store/apis/getChannelsForFilterSort.js +2 -1
  145. package/lib/commonjs/store/apis/getChannelsForFilterSort.js.map +1 -1
  146. package/lib/commonjs/store/apis/queries/selectMessagesForChannels.js +7 -5
  147. package/lib/commonjs/store/apis/queries/selectMessagesForChannels.js.map +1 -1
  148. package/lib/commonjs/utils/installNativeMultipartAdapter.js.map +1 -1
  149. package/lib/commonjs/version.json +1 -1
  150. package/lib/module/ai-components/README.md +50 -2
  151. package/lib/module/ai-components/index.js +24 -0
  152. package/lib/module/ai-components/index.js.map +1 -1
  153. package/lib/module/ai-components/message-composer/ComposerView.js +4 -2
  154. package/lib/module/ai-components/message-composer/ComposerView.js.map +1 -1
  155. package/lib/module/ai-components/message-composer/SpeechToTextButton.js +4 -1
  156. package/lib/module/ai-components/message-composer/SpeechToTextButton.js.map +1 -1
  157. package/lib/module/ai-components/native/AIDictation.js +6 -0
  158. package/lib/module/ai-components/native/AIDictation.js.map +1 -1
  159. package/lib/module/ai-components/store/dictation/store.js +4 -2
  160. package/lib/module/ai-components/store/dictation/store.js.map +1 -1
  161. package/lib/module/ai-components/store/dictation/useDictationState.js +4 -2
  162. package/lib/module/ai-components/store/dictation/useDictationState.js.map +1 -1
  163. package/lib/module/ai-components/store/index.js +22 -0
  164. package/lib/module/ai-components/store/index.js.map +1 -1
  165. package/lib/module/ai-components/theme.js +3 -0
  166. package/lib/module/ai-components/theme.js.map +1 -1
  167. package/lib/module/ai-components/transcription/useDictation.js +7 -3
  168. package/lib/module/ai-components/transcription/useDictation.js.map +1 -1
  169. package/lib/module/ai-components/transcription/useDictationAudioLevel.js +32 -0
  170. package/lib/module/ai-components/transcription/useDictationAudioLevel.js.map +1 -0
  171. package/lib/module/ai-components/voice-visualization/VoiceGlow.js +79 -0
  172. package/lib/module/ai-components/voice-visualization/VoiceGlow.js.map +1 -0
  173. package/lib/module/ai-components/voice-visualization/VoiceVisualizationView.js +147 -0
  174. package/lib/module/ai-components/voice-visualization/VoiceVisualizationView.js.map +1 -0
  175. package/lib/module/ai-components/voice-visualization/VoiceWave.js +58 -0
  176. package/lib/module/ai-components/voice-visualization/VoiceWave.js.map +1 -0
  177. package/lib/module/ai-components/voice-visualization/bounce.js +60 -0
  178. package/lib/module/ai-components/voice-visualization/bounce.js.map +1 -0
  179. package/lib/module/ai-components/voice-visualization/driver.js +208 -0
  180. package/lib/module/ai-components/voice-visualization/driver.js.map +1 -0
  181. package/lib/module/ai-components/voice-visualization/glowShader.js +218 -0
  182. package/lib/module/ai-components/voice-visualization/glowShader.js.map +1 -0
  183. package/lib/module/ai-components/voice-visualization/index.js +18 -0
  184. package/lib/module/ai-components/voice-visualization/index.js.map +1 -0
  185. package/lib/module/ai-components/voice-visualization/math.js +70 -0
  186. package/lib/module/ai-components/voice-visualization/math.js.map +1 -0
  187. package/lib/module/ai-components/voice-visualization/palette.js +47 -0
  188. package/lib/module/ai-components/voice-visualization/palette.js.map +1 -0
  189. package/lib/module/ai-components/voice-visualization/simplex.js +77 -0
  190. package/lib/module/ai-components/voice-visualization/simplex.js.map +1 -0
  191. package/lib/module/ai-components/voice-visualization/styles.js +33 -0
  192. package/lib/module/ai-components/voice-visualization/styles.js.map +1 -0
  193. package/lib/module/ai-components/voice-visualization/types.js +4 -0
  194. package/lib/module/ai-components/voice-visualization/types.js.map +1 -0
  195. package/lib/module/ai-components/voice-visualization/useReduceMotion.js +28 -0
  196. package/lib/module/ai-components/voice-visualization/useReduceMotion.js.map +1 -0
  197. package/lib/module/ai-components/voice-visualization/useVoiceVisualization.js +129 -0
  198. package/lib/module/ai-components/voice-visualization/useVoiceVisualization.js.map +1 -0
  199. package/lib/module/ai-components/voice-visualization/waveGeometry.js +124 -0
  200. package/lib/module/ai-components/voice-visualization/waveGeometry.js.map +1 -0
  201. package/lib/module/components/Channel/hooks/useChannelLifecycle.js +2 -1
  202. package/lib/module/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
  203. package/lib/module/components/ChannelDetails/components/members/ChannelMemberItem.js +1 -1
  204. package/lib/module/components/ChannelDetails/components/members/ChannelMemberItem.js.map +1 -1
  205. package/lib/module/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js +1 -1
  206. package/lib/module/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js.map +1 -1
  207. package/lib/module/components/ChannelList/ChannelListView.js +0 -2
  208. package/lib/module/components/ChannelList/ChannelListView.js.map +1 -1
  209. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js +1 -1
  210. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  211. package/lib/module/components/ChannelPreview/ChannelDetailsBottomSheet.js +1 -1
  212. package/lib/module/components/ChannelPreview/ChannelDetailsBottomSheet.js.map +1 -1
  213. package/lib/module/components/ChannelPreview/ChannelLastMessagePreview.js +1 -0
  214. package/lib/module/components/ChannelPreview/ChannelLastMessagePreview.js.map +1 -1
  215. package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +2 -0
  216. package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
  217. package/lib/module/components/ChannelPreview/ChannelPreviewTitle.js +1 -0
  218. package/lib/module/components/ChannelPreview/ChannelPreviewTitle.js.map +1 -1
  219. package/lib/module/components/ChannelPreview/ChannelPreviewView.js +1 -1
  220. package/lib/module/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
  221. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js +5 -5
  222. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
  223. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +1 -1
  224. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
  225. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js +1 -1
  226. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js.map +1 -1
  227. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +4 -1
  228. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
  229. package/lib/module/components/Chat/hooks/useConnectionLifecycle.js +6 -2
  230. package/lib/module/components/Chat/hooks/useConnectionLifecycle.js.map +1 -1
  231. package/lib/module/components/Indicators/LoadingErrorIndicator.js +5 -0
  232. package/lib/module/components/Indicators/LoadingErrorIndicator.js.map +1 -1
  233. package/lib/module/components/Message/Message.js +1 -1
  234. package/lib/module/components/Message/Message.js.map +1 -1
  235. package/lib/module/components/Message/MessageItemView/MessageStatus.js +4 -0
  236. package/lib/module/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  237. package/lib/module/components/Message/MessageItemView/MessageWrapper.js +7 -4
  238. package/lib/module/components/Message/MessageItemView/MessageWrapper.js.map +1 -1
  239. package/lib/module/components/Message/hooks/useCreateMessageActionHandlers.js +3 -3
  240. package/lib/module/components/Message/hooks/useCreateMessageActionHandlers.js.map +1 -1
  241. package/lib/module/components/Message/hooks/useProcessReactions.js +2 -2
  242. package/lib/module/components/Message/hooks/useProcessReactions.js.map +1 -1
  243. package/lib/module/components/Message/hooks/useUserMuteActive.js +1 -1
  244. package/lib/module/components/Message/hooks/useUserMuteActive.js.map +1 -1
  245. package/lib/module/components/MessageInput/MessageComposer.js +2 -1
  246. package/lib/module/components/MessageInput/MessageComposer.js.map +1 -1
  247. package/lib/module/components/MessageList/MessageFlashList.js +2 -2
  248. package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
  249. package/lib/module/components/MessageList/MessageList.js +2 -2
  250. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  251. package/lib/module/components/MessageList/hooks/useOwnUnreadCount.js +1 -1
  252. package/lib/module/components/MessageList/hooks/useOwnUnreadCount.js.map +1 -1
  253. package/lib/module/components/MessageMenu/MessageUserReactionsItem.js +1 -1
  254. package/lib/module/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
  255. package/lib/module/components/MessageMenu/hooks/useMessageOwnReactions.js +2 -2
  256. package/lib/module/components/MessageMenu/hooks/useMessageOwnReactions.js.map +1 -1
  257. package/lib/module/components/Poll/components/PollAnswersList.js +1 -1
  258. package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
  259. package/lib/module/components/Poll/components/PollButtons.js +1 -1
  260. package/lib/module/components/Poll/components/PollButtons.js.map +1 -1
  261. package/lib/module/components/Poll/hook/useIsPollCreatedByCurrentUser.js +1 -1
  262. package/lib/module/components/Poll/hook/useIsPollCreatedByCurrentUser.js.map +1 -1
  263. package/lib/module/components/Thread/Thread.js +1 -1
  264. package/lib/module/components/Thread/Thread.js.map +1 -1
  265. package/lib/module/components/ThreadList/ThreadListItem.js +1 -1
  266. package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -1
  267. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js +9 -2
  268. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js.map +1 -1
  269. package/lib/module/components/index.js +24 -0
  270. package/lib/module/components/index.js.map +1 -1
  271. package/lib/module/contexts/componentsContext/defaultComponents.js +0 -2
  272. package/lib/module/contexts/componentsContext/defaultComponents.js.map +1 -1
  273. package/lib/module/contexts/liveLocationManagerContext/LiveLocationManagerContext.js +1 -1
  274. package/lib/module/contexts/liveLocationManagerContext/LiveLocationManagerContext.js.map +1 -1
  275. package/lib/module/hooks/actions/useChannelActionItems.js +1 -1
  276. package/lib/module/hooks/actions/useChannelActionItems.js.map +1 -1
  277. package/lib/module/hooks/actions/useChannelActions.js +2 -2
  278. package/lib/module/hooks/actions/useChannelActions.js.map +1 -1
  279. package/lib/module/hooks/actions/useChannelMemberActionItems.js +1 -1
  280. package/lib/module/hooks/actions/useChannelMemberActionItems.js.map +1 -1
  281. package/lib/module/hooks/index.js +11 -0
  282. package/lib/module/hooks/index.js.map +1 -1
  283. package/lib/module/hooks/messagePreview/useMessageDeliveryStatus.js +6 -6
  284. package/lib/module/hooks/messagePreview/useMessageDeliveryStatus.js.map +1 -1
  285. package/lib/module/hooks/useIsDirectChat.js +1 -1
  286. package/lib/module/hooks/useIsDirectChat.js.map +1 -1
  287. package/lib/module/i18n/runtimeDefaults.js +1 -0
  288. package/lib/module/i18n/runtimeDefaults.js.map +1 -1
  289. package/lib/module/store/apis/getChannelMessages.js +5 -3
  290. package/lib/module/store/apis/getChannelMessages.js.map +1 -1
  291. package/lib/module/store/apis/getChannels.js +6 -3
  292. package/lib/module/store/apis/getChannels.js.map +1 -1
  293. package/lib/module/store/apis/getChannelsForFilterSort.js +2 -1
  294. package/lib/module/store/apis/getChannelsForFilterSort.js.map +1 -1
  295. package/lib/module/store/apis/queries/selectMessagesForChannels.js +7 -5
  296. package/lib/module/store/apis/queries/selectMessagesForChannels.js.map +1 -1
  297. package/lib/module/utils/installNativeMultipartAdapter.js.map +1 -1
  298. package/lib/module/version.json +1 -1
  299. package/lib/typescript/ai-components/index.d.ts +3 -1
  300. package/lib/typescript/ai-components/index.d.ts.map +1 -1
  301. package/lib/typescript/ai-components/message-composer/ComposerView.d.ts.map +1 -1
  302. package/lib/typescript/ai-components/native/AIDictation.d.ts +6 -0
  303. package/lib/typescript/ai-components/native/AIDictation.d.ts.map +1 -1
  304. package/lib/typescript/ai-components/store/dictation/store.d.ts +7 -0
  305. package/lib/typescript/ai-components/store/dictation/store.d.ts.map +1 -1
  306. package/lib/typescript/ai-components/store/dictation/useDictationState.d.ts +1 -0
  307. package/lib/typescript/ai-components/store/dictation/useDictationState.d.ts.map +1 -1
  308. package/lib/typescript/ai-components/store/index.d.ts +2 -0
  309. package/lib/typescript/ai-components/store/index.d.ts.map +1 -1
  310. package/lib/typescript/ai-components/theme.d.ts +13 -0
  311. package/lib/typescript/ai-components/theme.d.ts.map +1 -1
  312. package/lib/typescript/ai-components/transcription/useDictation.d.ts +9 -1
  313. package/lib/typescript/ai-components/transcription/useDictation.d.ts.map +1 -1
  314. package/lib/typescript/ai-components/transcription/useDictationAudioLevel.d.ts +11 -0
  315. package/lib/typescript/ai-components/transcription/useDictationAudioLevel.d.ts.map +1 -0
  316. package/lib/typescript/ai-components/voice-visualization/VoiceGlow.d.ts +13 -0
  317. package/lib/typescript/ai-components/voice-visualization/VoiceGlow.d.ts.map +1 -0
  318. package/lib/typescript/ai-components/voice-visualization/VoiceVisualizationView.d.ts +26 -0
  319. package/lib/typescript/ai-components/voice-visualization/VoiceVisualizationView.d.ts.map +1 -0
  320. package/lib/typescript/ai-components/voice-visualization/VoiceWave.d.ts +13 -0
  321. package/lib/typescript/ai-components/voice-visualization/VoiceWave.d.ts.map +1 -0
  322. package/lib/typescript/ai-components/voice-visualization/bounce.d.ts +20 -0
  323. package/lib/typescript/ai-components/voice-visualization/bounce.d.ts.map +1 -0
  324. package/lib/typescript/ai-components/voice-visualization/driver.d.ts +96 -0
  325. package/lib/typescript/ai-components/voice-visualization/driver.d.ts.map +1 -0
  326. package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts +7 -0
  327. package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts.map +1 -0
  328. package/lib/typescript/ai-components/voice-visualization/index.d.ts +4 -0
  329. package/lib/typescript/ai-components/voice-visualization/index.d.ts.map +1 -0
  330. package/lib/typescript/ai-components/voice-visualization/math.d.ts +26 -0
  331. package/lib/typescript/ai-components/voice-visualization/math.d.ts.map +1 -0
  332. package/lib/typescript/ai-components/voice-visualization/palette.d.ts +18 -0
  333. package/lib/typescript/ai-components/voice-visualization/palette.d.ts.map +1 -0
  334. package/lib/typescript/ai-components/voice-visualization/simplex.d.ts +11 -0
  335. package/lib/typescript/ai-components/voice-visualization/simplex.d.ts.map +1 -0
  336. package/lib/typescript/ai-components/voice-visualization/styles.d.ts +24 -0
  337. package/lib/typescript/ai-components/voice-visualization/styles.d.ts.map +1 -0
  338. package/lib/typescript/ai-components/voice-visualization/types.d.ts +45 -0
  339. package/lib/typescript/ai-components/voice-visualization/types.d.ts.map +1 -0
  340. package/lib/typescript/ai-components/voice-visualization/useReduceMotion.d.ts +10 -0
  341. package/lib/typescript/ai-components/voice-visualization/useReduceMotion.d.ts.map +1 -0
  342. package/lib/typescript/ai-components/voice-visualization/useVoiceVisualization.d.ts +18 -0
  343. package/lib/typescript/ai-components/voice-visualization/useVoiceVisualization.d.ts.map +1 -0
  344. package/lib/typescript/ai-components/voice-visualization/waveGeometry.d.ts +25 -0
  345. package/lib/typescript/ai-components/voice-visualization/waveGeometry.d.ts.map +1 -0
  346. package/lib/typescript/components/Channel/hooks/useChannelLifecycle.d.ts.map +1 -1
  347. package/lib/typescript/components/ChannelList/ChannelListView.d.ts +1 -1
  348. package/lib/typescript/components/ChannelList/ChannelListView.d.ts.map +1 -1
  349. package/lib/typescript/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.d.ts.map +1 -1
  350. package/lib/typescript/components/ChannelPreview/hooks/useChannelPreviewData.d.ts.map +1 -1
  351. package/lib/typescript/components/Chat/hooks/useConnectionLifecycle.d.ts +5 -4
  352. package/lib/typescript/components/Chat/hooks/useConnectionLifecycle.d.ts.map +1 -1
  353. package/lib/typescript/components/Indicators/LoadingErrorIndicator.d.ts +1 -1
  354. package/lib/typescript/components/Indicators/LoadingErrorIndicator.d.ts.map +1 -1
  355. package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts.map +1 -1
  356. package/lib/typescript/components/Message/MessageItemView/MessageWrapper.d.ts.map +1 -1
  357. package/lib/typescript/components/MessageInput/MessageComposer.d.ts.map +1 -1
  358. package/lib/typescript/components/ThreadList/ThreadListUnreadBanner.d.ts.map +1 -1
  359. package/lib/typescript/components/index.d.ts +2 -0
  360. package/lib/typescript/components/index.d.ts.map +1 -1
  361. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +0 -1
  362. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts.map +1 -1
  363. package/lib/typescript/hooks/index.d.ts +1 -0
  364. package/lib/typescript/hooks/index.d.ts.map +1 -1
  365. package/lib/typescript/hooks/messagePreview/useMessageDeliveryStatus.d.ts +3 -0
  366. package/lib/typescript/hooks/messagePreview/useMessageDeliveryStatus.d.ts.map +1 -1
  367. package/lib/typescript/i18n/keys.d.ts +3 -1
  368. package/lib/typescript/i18n/keys.d.ts.map +1 -1
  369. package/lib/typescript/i18n/runtimeDefaults.d.ts +1 -0
  370. package/lib/typescript/i18n/runtimeDefaults.d.ts.map +1 -1
  371. package/lib/typescript/store/apis/getChannelMessages.d.ts +2 -1
  372. package/lib/typescript/store/apis/getChannelMessages.d.ts.map +1 -1
  373. package/lib/typescript/store/apis/getChannels.d.ts +3 -1
  374. package/lib/typescript/store/apis/getChannels.d.ts.map +1 -1
  375. package/lib/typescript/store/apis/getChannelsForFilterSort.d.ts.map +1 -1
  376. package/lib/typescript/store/apis/queries/selectMessagesForChannels.d.ts +3 -1
  377. package/lib/typescript/store/apis/queries/selectMessagesForChannels.d.ts.map +1 -1
  378. package/lib/typescript/utils/installNativeMultipartAdapter.d.ts +5 -3
  379. package/lib/typescript/utils/installNativeMultipartAdapter.d.ts.map +1 -1
  380. package/package.json +4 -4
  381. package/src/__tests__/offline-support/offline-feature.tsx +7 -7
  382. package/src/__tests__/offline-support/optimistic-update.tsx +5 -5
  383. package/src/ai-components/README.md +50 -2
  384. package/src/ai-components/__tests__/VoiceVisualization.test.tsx +208 -0
  385. package/src/ai-components/__tests__/dictation.test.tsx +58 -1
  386. package/src/ai-components/__tests__/voiceDriver.test.ts +317 -0
  387. package/src/ai-components/__tests__/voicePalette.test.ts +124 -0
  388. package/src/ai-components/index.ts +3 -1
  389. package/src/ai-components/message-composer/ComposerView.tsx +6 -3
  390. package/src/ai-components/message-composer/SpeechToTextButton.tsx +5 -2
  391. package/src/ai-components/native/AIDictation.ts +11 -0
  392. package/src/ai-components/store/dictation/store.ts +9 -0
  393. package/src/ai-components/store/dictation/useDictationState.ts +2 -1
  394. package/src/ai-components/store/index.ts +2 -0
  395. package/src/ai-components/theme.ts +16 -0
  396. package/src/ai-components/transcription/useDictation.ts +16 -3
  397. package/src/ai-components/transcription/useDictationAudioLevel.ts +38 -0
  398. package/src/ai-components/voice-visualization/VoiceGlow.tsx +79 -0
  399. package/src/ai-components/voice-visualization/VoiceVisualizationView.tsx +179 -0
  400. package/src/ai-components/voice-visualization/VoiceWave.tsx +67 -0
  401. package/src/ai-components/voice-visualization/bounce.ts +61 -0
  402. package/src/ai-components/voice-visualization/driver.ts +240 -0
  403. package/src/ai-components/voice-visualization/glowShader.ts +227 -0
  404. package/src/ai-components/voice-visualization/index.ts +3 -0
  405. package/src/ai-components/voice-visualization/math.ts +73 -0
  406. package/src/ai-components/voice-visualization/palette.ts +59 -0
  407. package/src/ai-components/voice-visualization/simplex.ts +92 -0
  408. package/src/ai-components/voice-visualization/styles.ts +42 -0
  409. package/src/ai-components/voice-visualization/types.ts +44 -0
  410. package/src/ai-components/voice-visualization/useReduceMotion.ts +34 -0
  411. package/src/ai-components/voice-visualization/useVoiceVisualization.ts +153 -0
  412. package/src/ai-components/voice-visualization/waveGeometry.ts +137 -0
  413. package/src/components/Channel/__tests__/Channel.test.tsx +44 -5
  414. package/src/components/Channel/hooks/useChannelLifecycle.ts +4 -1
  415. package/src/components/ChannelDetails/__tests__/ChannelDetails.test.tsx +1 -1
  416. package/src/components/ChannelDetails/__tests__/ChannelDetailsActionsSection.test.tsx +1 -1
  417. package/src/components/ChannelDetails/__tests__/ChannelDetailsEditButton.test.tsx +1 -1
  418. package/src/components/ChannelDetails/__tests__/ChannelDetailsMemberSection.test.tsx +1 -1
  419. package/src/components/ChannelDetails/__tests__/ChannelDetailsNavHeader.test.tsx +1 -1
  420. package/src/components/ChannelDetails/__tests__/ChannelDetailsProfile.test.tsx +2 -2
  421. package/src/components/ChannelDetails/__tests__/ChannelEditDetailsForm.test.tsx +1 -1
  422. package/src/components/ChannelDetails/__tests__/ChannelEditDetailsFormContent.test.tsx +2 -2
  423. package/src/components/ChannelDetails/__tests__/ChannelEditName.test.tsx +1 -1
  424. package/src/components/ChannelDetails/__tests__/members/ChannelAddMembersForm.test.tsx +1 -1
  425. package/src/components/ChannelDetails/__tests__/members/ChannelMemberActionsSheet.test.tsx +2 -2
  426. package/src/components/ChannelDetails/__tests__/members/ChannelMemberItem.test.tsx +1 -1
  427. package/src/components/ChannelDetails/__tests__/useChannelDetailsMemberStatusText.test.tsx +1 -1
  428. package/src/components/ChannelDetails/components/members/ChannelMemberItem.tsx +1 -1
  429. package/src/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.ts +1 -1
  430. package/src/components/ChannelList/ChannelListView.tsx +1 -3
  431. package/src/components/ChannelList/__tests__/ChannelList.test.tsx +44 -0
  432. package/src/components/ChannelList/hooks/usePaginatedChannels.ts +1 -1
  433. package/src/components/ChannelPreview/ChannelDetailsBottomSheet.tsx +1 -1
  434. package/src/components/ChannelPreview/ChannelLastMessagePreview.tsx +1 -1
  435. package/src/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.tsx +8 -2
  436. package/src/components/ChannelPreview/ChannelPreviewTitle.tsx +1 -1
  437. package/src/components/ChannelPreview/ChannelPreviewView.tsx +1 -1
  438. package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx +4 -4
  439. package/src/components/ChannelPreview/__tests__/ChannelPreviewView.test.tsx +40 -0
  440. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewData.test.tsx +51 -0
  441. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayName.test.tsx +1 -1
  442. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayPresence.test.tsx +1 -1
  443. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewPollLabel.test.tsx +87 -0
  444. package/src/components/ChannelPreview/hooks/useChannelPreviewData.ts +3 -4
  445. package/src/components/ChannelPreview/hooks/useChannelPreviewDisplayName.ts +1 -1
  446. package/src/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.ts +1 -1
  447. package/src/components/ChannelPreview/hooks/useChannelPreviewPollLabel.ts +1 -1
  448. package/src/components/Chat/__tests__/Chat.test.tsx +31 -1
  449. package/src/components/Chat/hooks/useConnectionLifecycle.ts +12 -6
  450. package/src/components/Indicators/LoadingErrorIndicator.tsx +8 -1
  451. package/src/components/Message/Message.tsx +1 -1
  452. package/src/components/Message/MessageItemView/MessageStatus.tsx +16 -2
  453. package/src/components/Message/MessageItemView/MessageWrapper.tsx +14 -4
  454. package/src/components/Message/MessageItemView/__tests__/MessageStatus.test.tsx +21 -0
  455. package/src/components/Message/hooks/__tests__/useCreateMessageActionHandlers.test.tsx +1 -1
  456. package/src/components/Message/hooks/__tests__/useUserMuteActive.test.tsx +1 -1
  457. package/src/components/Message/hooks/useCreateMessageActionHandlers.ts +3 -3
  458. package/src/components/Message/hooks/useProcessReactions.ts +2 -2
  459. package/src/components/Message/hooks/useUserMuteActive.ts +1 -1
  460. package/src/components/MessageInput/MessageComposer.tsx +4 -2
  461. package/src/components/MessageList/MessageFlashList.tsx +2 -2
  462. package/src/components/MessageList/MessageList.tsx +2 -2
  463. package/src/components/MessageList/__tests__/MessageList.test.tsx +26 -8
  464. package/src/components/MessageList/__tests__/ScrollToBottomButton.test.tsx +3 -3
  465. package/src/components/MessageList/hooks/useOwnUnreadCount.ts +1 -1
  466. package/src/components/MessageMenu/MessageUserReactionsItem.tsx +1 -1
  467. package/src/components/MessageMenu/hooks/useMessageOwnReactions.ts +2 -2
  468. package/src/components/Poll/components/PollAnswersList.tsx +1 -1
  469. package/src/components/Poll/components/PollButtons.tsx +1 -1
  470. package/src/components/Poll/hook/useIsPollCreatedByCurrentUser.ts +1 -1
  471. package/src/components/Thread/Thread.tsx +1 -1
  472. package/src/components/Thread/__tests__/Thread.test.tsx +4 -2
  473. package/src/components/ThreadList/ThreadListItem.tsx +1 -1
  474. package/src/components/ThreadList/ThreadListUnreadBanner.tsx +6 -2
  475. package/src/components/ThreadList/__tests__/ThreadList.test.tsx +21 -0
  476. package/src/components/index.ts +2 -0
  477. package/src/contexts/componentsContext/defaultComponents.ts +0 -2
  478. package/src/contexts/liveLocationManagerContext/LiveLocationManagerContext.tsx +1 -1
  479. package/src/hooks/__tests__/useChannelMuteActive.test.tsx +1 -1
  480. package/src/hooks/__tests__/useIsDirectChat.test.tsx +1 -1
  481. package/src/hooks/actions/__tests__/useChannelActionItems.test.tsx +1 -1
  482. package/src/hooks/actions/__tests__/useChannelActions.test.tsx +1 -1
  483. package/src/hooks/actions/__tests__/useChannelMemberActionItems.test.tsx +6 -6
  484. package/src/hooks/actions/useChannelActionItems.tsx +1 -1
  485. package/src/hooks/actions/useChannelActions.ts +2 -2
  486. package/src/hooks/actions/useChannelMemberActionItems.tsx +1 -1
  487. package/src/hooks/index.ts +1 -0
  488. package/src/hooks/messagePreview/__tests__/useMessageDeliveryStatus.test.tsx +57 -32
  489. package/src/hooks/messagePreview/useMessageDeliveryStatus.ts +13 -7
  490. package/src/hooks/useIsDirectChat.ts +1 -1
  491. package/src/i18n/__tests__/catalog.fixture.json +2 -0
  492. package/src/i18n/keys.ts +3 -0
  493. package/src/i18n/runtimeDefaults.ts +1 -0
  494. package/src/store/__tests__/selectMessagesForChannels.test.ts +34 -12
  495. package/src/store/apis/getChannelMessages.ts +4 -1
  496. package/src/store/apis/getChannels.ts +5 -1
  497. package/src/store/apis/getChannelsForFilterSort.ts +1 -0
  498. package/src/store/apis/queries/selectMessagesForChannels.ts +7 -2
  499. package/src/utils/installNativeMultipartAdapter.ts +5 -3
  500. package/src/version.json +1 -1
@@ -0,0 +1,153 @@
1
+ import { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { useColorScheme } from 'react-native';
3
+
4
+ import {
5
+ type FrameInfo,
6
+ runOnUI,
7
+ useFrameCallback,
8
+ useSharedValue,
9
+ withTiming,
10
+ } from 'react-native-reanimated';
11
+
12
+ import {
13
+ advanceDriver,
14
+ createDriverState,
15
+ hoverDriver,
16
+ tapDriver,
17
+ type VoiceDriverInput,
18
+ } from './driver';
19
+ import { audioLevelInput, expressivityInput } from './math';
20
+ import { FALLBACK_COLOR, luminance, toHsvPalette } from './palette';
21
+ import type { VoiceVisualizationStyle } from './styles';
22
+ import type { VoiceVisualizationProps } from './types';
23
+ import { useReduceMotion } from './useReduceMotion';
24
+
25
+ import { useTheme } from '../contexts';
26
+
27
+ /** How long the visualization takes to fade in once its first frame is drawn, as on iOS. */
28
+ const FADE_IN_MS = 300;
29
+
30
+ /** Whether to draw for a dark background: the theme's backdrop when readable, else the scheme. */
31
+ const useDark = (backdropColor: string | undefined) => {
32
+ const scheme = useColorScheme();
33
+ return useMemo(() => {
34
+ const backdrop = backdropColor ? luminance(backdropColor) : null;
35
+ if (backdrop !== null) return backdrop < 0.5 ? 1 : 0;
36
+ return scheme === 'dark' ? 1 : 0;
37
+ }, [backdropColor, scheme]);
38
+ };
39
+
40
+ /**
41
+ * A voice visualization's motion, advanced once a frame on the UI thread.
42
+ *
43
+ * The driver lives in a shared value the renderers derive from, so a frame redraws without
44
+ * re-rendering React, and `audioLevel` is read in the frame callback, so a level changing many
45
+ * times a second re-renders nothing either.
46
+ */
47
+ export const useVoiceVisualization = (
48
+ { audioLevel, colors, expressivity, state }: VoiceVisualizationProps,
49
+ style: VoiceVisualizationStyle,
50
+ ) => {
51
+ const { theme } = useTheme();
52
+ const reduceMotion = useReduceMotion();
53
+ const themeColors = theme.voiceVisualization?.colors;
54
+ const dark = useDark(theme.voiceVisualization?.backdropColor);
55
+
56
+ const colorsKey = (colors ?? []).join('|');
57
+ const themeKey = (themeColors ?? []).join('|');
58
+ const palette = useMemo(() => {
59
+ const own = toHsvPalette(colors ?? []);
60
+ if (own.length) return own;
61
+ const fromTheme = toHsvPalette(themeColors ?? []);
62
+ return fromTheme.length ? fromTheme : toHsvPalette([FALLBACK_COLOR]);
63
+ // Keyed by the colors themselves, so a new array of the same colors changes nothing.
64
+ // eslint-disable-next-line react-hooks/exhaustive-deps
65
+ }, [colorsKey, themeKey]);
66
+
67
+ const input: VoiceDriverInput = {
68
+ audioLevel: null,
69
+ colors: palette,
70
+ expressivity: expressivityInput(expressivity),
71
+ reduceMotion,
72
+ state,
73
+ };
74
+
75
+ // Starts settled on the first input, so the first frame shows it rather than easing into it.
76
+ const [initialDriver] = useState(() => createDriverState(input, style.levelResponse));
77
+ const driver = useSharedValue(initialDriver);
78
+ const inputValue = useSharedValue(input);
79
+ const darkValue = useSharedValue(dark);
80
+ const opacity = useSharedValue(0);
81
+ const fadingIn = useSharedValue(false);
82
+
83
+ useEffect(() => {
84
+ inputValue.value = {
85
+ audioLevel: null,
86
+ colors: palette,
87
+ expressivity: expressivityInput(expressivity),
88
+ reduceMotion,
89
+ state,
90
+ };
91
+ }, [expressivity, inputValue, palette, reduceMotion, state]);
92
+
93
+ useEffect(() => {
94
+ darkValue.value = dark;
95
+ }, [dark, darkValue]);
96
+
97
+ const onFrame = useCallback(
98
+ (frame: FrameInfo) => {
99
+ 'worklet';
100
+ const seconds = (frame.timeSincePreviousFrame ?? 0) / 1000;
101
+ const current = inputValue.value;
102
+ const level = audioLevelInput(audioLevel ? audioLevel.value : null);
103
+ const next = level === current.audioLevel ? current : { ...current, audioLevel: level };
104
+ driver.modify((d) => {
105
+ 'worklet';
106
+ advanceDriver(d, next, seconds);
107
+ return d;
108
+ });
109
+ // It fades in once it has something to show, rather than flashing empty while the first
110
+ // frame lands.
111
+ if (!fadingIn.value) {
112
+ fadingIn.value = true;
113
+ opacity.value = withTiming(1, { duration: FADE_IN_MS });
114
+ }
115
+ },
116
+ [audioLevel, driver, fadingIn, inputValue, opacity],
117
+ );
118
+ useFrameCallback(onFrame);
119
+
120
+ /** A tap at a point centred and normalized to the shorter side, y up. */
121
+ const tap = useCallback(
122
+ (x: number, y: number) => {
123
+ runOnUI((tapX: number, tapY: number) => {
124
+ 'worklet';
125
+ driver.modify((d) => {
126
+ 'worklet';
127
+ tapDriver(d, inputValue.value, tapX, tapY);
128
+ return d;
129
+ });
130
+ })(x, y);
131
+ },
132
+ [driver, inputValue],
133
+ );
134
+
135
+ /** Where a pointer hovers, or null once it has left. */
136
+ const hover = useCallback(
137
+ (x: number | null, y: number | null) => {
138
+ runOnUI((hoverX: number | null, hoverY: number | null) => {
139
+ 'worklet';
140
+ driver.modify((d) => {
141
+ 'worklet';
142
+ hoverDriver(d, inputValue.value, hoverX, hoverY);
143
+ return d;
144
+ });
145
+ })(x, y);
146
+ },
147
+ [driver, inputValue],
148
+ );
149
+
150
+ return { dark: darkValue, driver, hover, opacity, tap };
151
+ };
152
+
153
+ export type VoiceVisualization = ReturnType<typeof useVoiceVisualization>;
@@ -0,0 +1,137 @@
1
+ import { Skia, type SkPath } from '@shopify/react-native-skia';
2
+
3
+ import { driverReact, driverScale, type VoiceDriverState } from './driver';
4
+ import { clamp, mix, saturate, smoothstep, speechEnv, vivid } from './math';
5
+
6
+ const POINTS = 160;
7
+ export const WAVE_STOPS = 13;
8
+ export const WAVE_START = -0.42;
9
+ export const WAVE_END = 0.42;
10
+
11
+ /** The stops' positions along the gradient, evenly spaced. */
12
+ export const WAVE_POSITIONS = Array.from({ length: WAVE_STOPS }, (_, s) => s / (WAVE_STOPS - 1));
13
+
14
+ const sq = (x: number) => {
15
+ 'worklet';
16
+ return x * x;
17
+ };
18
+
19
+ const bandCenter = (i: number, seg: number, t: number) => {
20
+ 'worklet';
21
+ switch (i) {
22
+ case 0:
23
+ return 0.5 * seg + 0.12 * Math.sin(t * 0.13);
24
+ case 1:
25
+ return 1.5 * seg + 0.13 * Math.sin(t * 0.11 + 2.1);
26
+ case 2:
27
+ return 2.5 * seg + 0.12 * Math.sin(t * 0.17 + 4.2);
28
+ case 3:
29
+ return 3.5 * seg + 0.12 * Math.sin(t * 0.15 + 1);
30
+ default:
31
+ return 4.5 * seg + 0.12 * Math.sin(t * 0.1 + 3.3);
32
+ }
33
+ };
34
+
35
+ /** The string's height above its centre line at each point, centred and normalized, y up. */
36
+ export const waveHeights = (d: VoiceDriverState) => {
37
+ 'worklet';
38
+ const t = d.time;
39
+ const react = driverReact(d);
40
+ const center = d.load * 0.3 * Math.sin(t * 1.3);
41
+ const freq = 24 + 26 * react + 10 * d.flow;
42
+ const speak = smoothstep(0.6, 1, react);
43
+ const drive = mix(speak * 0.26 * speechEnv(t), 0.3 * d.mic, d.voice);
44
+ const reach = 0.018 + 0.022 * react + drive + 0.14 * d.flow + 0.1 * d.load;
45
+ const tapAlive = d.tapAge < 1.5;
46
+ const hovered = d.hoverAmount > 0.001;
47
+ const xs: number[] = [];
48
+ const ys: number[] = [];
49
+ for (let i = 0; i <= POINTS; i++) {
50
+ const x = WAVE_START + ((WAVE_END - WAVE_START) * i) / POINTS;
51
+ let y = Math.exp(-sq((x - center) / 0.18)) * reach * Math.sin(x * freq - t * 5);
52
+ if (tapAlive) {
53
+ y +=
54
+ Math.exp(-sq((x - d.tapX) / 0.12)) *
55
+ Math.sin((x - d.tapX) * 40 - d.tapAge * 24) *
56
+ Math.exp(-d.tapAge * 2.6) *
57
+ 0.05;
58
+ }
59
+ if (hovered) {
60
+ y +=
61
+ Math.exp(-sq((x - d.hoverX) / 0.12)) *
62
+ Math.sin((x - d.hoverX) * 40 - t * 7) *
63
+ d.hoverAmount *
64
+ 0.04;
65
+ }
66
+ xs.push(x);
67
+ ys.push(y);
68
+ }
69
+ return { xs, ys };
70
+ };
71
+
72
+ type LegacyPath = SkPath & {
73
+ lineTo: (x: number, y: number) => void;
74
+ moveTo: (x: number, y: number) => void;
75
+ };
76
+
77
+ /**
78
+ * The wave as a path in a `width` by `height` canvas. Newer Skia versions build immutable paths with
79
+ * `PathBuilder`; earlier 2.x versions mutate `SkPath` itself.
80
+ */
81
+ export const wavePath = (d: VoiceDriverState, width: number, height: number): SkPath => {
82
+ 'worklet';
83
+ const m = Math.min(width, height);
84
+ const scale = driverScale(d);
85
+ const { xs, ys } = waveHeights(d);
86
+ const px = (i: number) => width / 2 + xs[i] * m * scale;
87
+ const py = (i: number) => height / 2 - ys[i] * m * scale;
88
+ if (Skia.PathBuilder) {
89
+ const builder = Skia.PathBuilder.Make();
90
+ builder.moveTo(px(0), py(0));
91
+ for (let i = 1; i < xs.length; i++) builder.lineTo(px(i), py(i));
92
+ return builder.build();
93
+ }
94
+ const path = Skia.Path.Make() as LegacyPath;
95
+ path.moveTo(px(0), py(0));
96
+ for (let i = 1; i < xs.length; i++) path.lineTo(px(i), py(i));
97
+ return path;
98
+ };
99
+
100
+ /**
101
+ * The string's colors at its gradient stops, as RGBA from 0 to 1: up to five bands drifting along
102
+ * it, weighed by how near their centres are, its ends fading out.
103
+ */
104
+ export const waveColors = (d: VoiceDriverState, dark: number) => {
105
+ 'worklet';
106
+ const band: number[][] = [];
107
+ for (let i = 0; i < 5; i++) {
108
+ const rgb = [0, 0, 0];
109
+ vivid(d.colors[i], dark, rgb);
110
+ band.push(rgb);
111
+ }
112
+ const seg = 1 / Math.max(d.count, 1);
113
+ const bandWidth = seg * 0.7;
114
+ const stops: Float32Array[] = [];
115
+ for (let s = 0; s < WAVE_STOPS; s++) {
116
+ const x = WAVE_START + ((WAVE_END - WAVE_START) * s) / (WAVE_STOPS - 1);
117
+ const gx = clamp(x + 0.5, 0, 1);
118
+ const color = [0, 0, 0];
119
+ let total = 1e-4;
120
+ for (let i = 0; i < 5; i++) {
121
+ const present = i === 0 ? 1 : clamp(d.count - i, 0, 1);
122
+ const weight = Math.exp(-sq((gx - bandCenter(i, seg, d.time)) / bandWidth)) * present;
123
+ for (let j = 0; j < 3; j++) color[j] += band[i][j] * weight;
124
+ total += weight;
125
+ }
126
+ for (let j = 0; j < 3; j++) color[j] /= total;
127
+ saturate(color, 1.2);
128
+ saturate(color, d.sat);
129
+ const alpha = clamp(smoothstep(0.4, 0.24, Math.abs(x)) * d.bright, 0, 1);
130
+ stops.push(new Float32Array([color[0], color[1], color[2], alpha]));
131
+ }
132
+ return stops;
133
+ };
134
+
135
+ /** The line is 0.6% of the shorter side, and never under 1.5 device pixels, as on Android and iOS. */
136
+ export const waveStrokeWidth = (width: number, height: number, pixelRatio: number) =>
137
+ Math.max(0.006 * Math.min(width, height), 1.5 / pixelRatio);
@@ -3,7 +3,7 @@ import { AppState, AppStateStatus, View } from 'react-native';
3
3
 
4
4
  import { act, cleanup, render, waitFor } from '@testing-library/react-native';
5
5
  import type { Channel as ChannelType, StreamChat as StreamChatType } from 'stream-chat';
6
- import { asTimestampNS, StreamChat } from 'stream-chat';
6
+ import { asTimestampNS, ChannelWatchStatus, StreamChat } from 'stream-chat';
7
7
 
8
8
  import type { ChannelContextValue } from '../../../contexts/channelContext/ChannelContext';
9
9
  import { ChannelContext, ChannelProvider } from '../../../contexts/channelContext/ChannelContext';
@@ -153,7 +153,7 @@ describe('Channel', () => {
153
153
  } as never);
154
154
  draft.initialized = true;
155
155
  const watchSpy = jest.spyOn(draft, 'watch');
156
- const reloadSpy = jest.spyOn(draft.messagePaginator, 'reload');
156
+ const seedSpy = jest.spyOn(draft.messagePaginator, 'reload');
157
157
 
158
158
  renderComponent({ channel: draft });
159
159
  // let the mount effect run to its end
@@ -163,7 +163,7 @@ describe('Channel', () => {
163
163
 
164
164
  expect(draft.isProvisional).toBe(true);
165
165
  expect(watchSpy).not.toHaveBeenCalled();
166
- expect(reloadSpy).not.toHaveBeenCalled();
166
+ expect(seedSpy).not.toHaveBeenCalled();
167
167
  });
168
168
 
169
169
  it('should set an error if channel watch fails and render a LoadingErrorIndicator', async () => {
@@ -757,6 +757,45 @@ describe('Channel initial load useEffect', () => {
757
757
  await waitFor(() => expect(Object.keys(channel.state.members)).toHaveLength(10));
758
758
  });
759
759
 
760
+ describe('watch on mount for an initialized channel at the head', () => {
761
+ const mountLoadedChannel = async (watchStatus: ChannelWatchStatus) => {
762
+ const messages = Array.from({ length: 10 }, (_, i) => generateMessage({ id: String(i) }));
763
+ const mockedChannel = generateChannelResponse({ messages });
764
+ useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
765
+ const channel = chatClient.channel('messaging', mockedChannel.channel.id);
766
+ await channel.watch();
767
+ jest.spyOn(channel.messagePaginator, 'isActiveIntervalAtHead', 'get').mockReturnValue(true);
768
+ channel.watchStatus = watchStatus;
769
+ const watchSpy = jest.fn();
770
+ channel.watch = watchSpy;
771
+ const seedSpy = jest.spyOn(channel.messagePaginator, 'seedUnreadSnapshot');
772
+
773
+ renderComponent({ channel, markReadOnMount: false });
774
+ // The seed runs after the watch decision, so the mount effect has made it by then.
775
+ await waitFor(() => expect(seedSpy).toHaveBeenCalled());
776
+
777
+ return watchSpy;
778
+ };
779
+
780
+ it('watches a channel that was loaded without a watch, such as a search result', async () => {
781
+ const watchSpy = await mountLoadedChannel(ChannelWatchStatus.NotWatching);
782
+
783
+ expect(watchSpy).toHaveBeenCalledTimes(1);
784
+ });
785
+
786
+ it('does not watch a channel that is already watched', async () => {
787
+ const watchSpy = await mountLoadedChannel(ChannelWatchStatus.Watching);
788
+
789
+ expect(watchSpy).not.toHaveBeenCalled();
790
+ });
791
+
792
+ it('leaves a channel whose watch dropped with the connection to the connection recovery', async () => {
793
+ const watchSpy = await mountLoadedChannel(ChannelWatchStatus.WasWatching);
794
+
795
+ expect(watchSpy).not.toHaveBeenCalled();
796
+ });
797
+ });
798
+
760
799
  describe('message page size', () => {
761
800
  const mountChannel = async () => {
762
801
  const mockedChannel = generateChannelResponse({ messages: [] });
@@ -979,7 +1018,7 @@ describe('Channel initial load useEffect', () => {
979
1018
  await channel.watch();
980
1019
 
981
1020
  // Call through to the real reload so the resync actually reconciles (and we can assert it ran).
982
- const reloadSpy = jest.spyOn(channel, 'reload');
1021
+ const seedSpy = jest.spyOn(channel, 'reload');
983
1022
 
984
1023
  renderComponent({ channel });
985
1024
 
@@ -1007,7 +1046,7 @@ describe('Channel initial load useEffect', () => {
1007
1046
  // The reload re-watches and reconciles, but preserves the failed (locally-unsent) messages —
1008
1047
  // the 10 originals + 10 failed remain.
1009
1048
  await waitFor(() => {
1010
- expect(reloadSpy).toHaveBeenCalled();
1049
+ expect(seedSpy).toHaveBeenCalled();
1011
1050
  expect(channel.messagePaginator.anchoredHeadItems.length).toBe(20);
1012
1051
  });
1013
1052
  });
@@ -1,5 +1,6 @@
1
1
  import { useEffect } from 'react';
2
2
 
3
+ import { ChannelWatchStatus } from 'stream-chat';
3
4
  import type { Channel, StreamChat } from 'stream-chat';
4
5
 
5
6
  import type { TranslationContextValue } from '../../../contexts/translationContext/TranslationContext';
@@ -107,7 +108,9 @@ export const useChannelLifecycle = ({
107
108
  }
108
109
 
109
110
  if (
110
- (!channel.initialized || !channel.messagePaginator.isActiveIntervalAtHead) &&
111
+ (!channel.initialized ||
112
+ !channel.messagePaginator.isActiveIntervalAtHead ||
113
+ channel.watchStatus === ChannelWatchStatus.NotWatching) &&
111
114
  initializeOnMount
112
115
  ) {
113
116
  try {
@@ -28,7 +28,7 @@ const Providers = ({ children }: PropsWithChildren) => (
28
28
  }}
29
29
  >
30
30
  <ChatContext.Provider
31
- value={{ client: { notifications: new NotificationManager(), userID: 'me' } } as never}
31
+ value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
32
32
  >
33
33
  {children}
34
34
  </ChatContext.Provider>
@@ -69,7 +69,7 @@ const sectionElement = () => (
69
69
  mutedUsers: [],
70
70
  mutedUsersStore,
71
71
  on: () => ({ unsubscribe: () => undefined }),
72
- userID: 'me',
72
+ userId: 'me',
73
73
  },
74
74
  } as never
75
75
  }
@@ -63,7 +63,7 @@ const Providers = ({ children }: PropsWithChildren) => (
63
63
  }}
64
64
  >
65
65
  <ChatContext.Provider
66
- value={{ client: { notifications: new NotificationManager(), userID: 'me' } } as never}
66
+ value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
67
67
  >
68
68
  {children}
69
69
  </ChatContext.Provider>
@@ -99,7 +99,7 @@ const renderSection = ({
99
99
  mutedUsers: [],
100
100
  notifications: new NotificationManager(),
101
101
  on: () => ({ unsubscribe: () => undefined }),
102
- userID: 'me',
102
+ userId: 'me',
103
103
  },
104
104
  } as never
105
105
  }
@@ -39,7 +39,7 @@ const Providers = ({ children }: PropsWithChildren) => (
39
39
  }}
40
40
  >
41
41
  <ChatContext.Provider
42
- value={{ client: { notifications: new NotificationManager(), userID: 'me' } } as never}
42
+ value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
43
43
  >
44
44
  {children}
45
45
  </ChatContext.Provider>
@@ -31,7 +31,7 @@ const buildChannel = () =>
31
31
  ({
32
32
  cid: 'messaging:test',
33
33
  data: {},
34
- getClient: () => ({ userID: OWN_USER_ID }),
34
+ getClient: () => ({ userId: OWN_USER_ID }),
35
35
  on: () => ({ unsubscribe: () => undefined }),
36
36
  }) as unknown as Channel;
37
37
 
@@ -45,7 +45,7 @@ const renderProfile = ({ channel = buildChannel() }: { channel?: Channel } = {})
45
45
  userLanguage: 'en',
46
46
  }}
47
47
  >
48
- <ChatProvider value={{ client: { userID: OWN_USER_ID } } as never}>
48
+ <ChatProvider value={{ client: { userId: OWN_USER_ID } } as never}>
49
49
  <ChannelDetailsContextProvider channel={channel}>
50
50
  <ChannelDetailsProfile />
51
51
  </ChannelDetailsContextProvider>
@@ -73,7 +73,7 @@ const renderForm = ({ channel, onClose = jest.fn() }: { channel: Channel; onClos
73
73
  }}
74
74
  >
75
75
  <ChatContext.Provider
76
- value={{ client: { notifications: new NotificationManager(), userID: 'me' } } as never}
76
+ value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
77
77
  >
78
78
  <ChannelDetailsContextProvider channel={channel}>
79
79
  <WithComponents overrides={{ ChannelEditDetailsFormContent: EditDetailsProbe }}>
@@ -80,7 +80,7 @@ const renderComponent = ({ channel }: { channel: Channel }) => {
80
80
  >
81
81
  <ChatContext.Provider
82
82
  value={
83
- { client: { on: () => ({ unsubscribe: () => undefined }), userID: 'me' } } as never
83
+ { client: { on: () => ({ unsubscribe: () => undefined }), userId: 'me' } } as never
84
84
  }
85
85
  >
86
86
  <ChannelDetailsContextProvider channel={channel}>
@@ -128,7 +128,7 @@ describe('ChannelEditDetailsFormContent', () => {
128
128
  >
129
129
  <ChatContext.Provider
130
130
  value={
131
- { client: { on: () => ({ unsubscribe: () => undefined }), userID: 'me' } } as never
131
+ { client: { on: () => ({ unsubscribe: () => undefined }), userId: 'me' } } as never
132
132
  }
133
133
  >
134
134
  <ChannelDetailsContextProvider channel={buildChannel()}>
@@ -35,7 +35,7 @@ const renderComponent = ({ channel }: { channel: Channel }) => {
35
35
  >
36
36
  <ChatContext.Provider
37
37
  value={
38
- { client: { on: () => ({ unsubscribe: () => undefined }), userID: 'me' } } as never
38
+ { client: { on: () => ({ unsubscribe: () => undefined }), userId: 'me' } } as never
39
39
  }
40
40
  >
41
41
  <ChannelEditDetailsContext.Provider value={{ store, submit: jest.fn() }}>
@@ -102,7 +102,7 @@ const renderForm = ({
102
102
  }}
103
103
  >
104
104
  <ChatContext.Provider
105
- value={{ client: { notifications: new NotificationManager(), userID: 'me' } } as never}
105
+ value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
106
106
  >
107
107
  <ChannelDetailsContextProvider channel={applyCapabilities(channel, capabilities)}>
108
108
  <WithComponents overrides={{ ChannelAddMembersFormContent: AddMembersProbe }}>
@@ -76,7 +76,7 @@ const renderSheet = ({
76
76
  client: {
77
77
  mutedUsers: [],
78
78
  on: () => ({ unsubscribe: () => undefined }),
79
- userID: 'me',
79
+ userId: 'me',
80
80
  },
81
81
  } as never
82
82
  }
@@ -156,7 +156,7 @@ describe('ChannelMemberActionsSheet', () => {
156
156
  <ChatContext.Provider
157
157
  value={
158
158
  {
159
- client: { userID: 'me', mutedUsers: [], on: () => ({ unsubscribe: () => undefined }) },
159
+ client: { userId: 'me', mutedUsers: [], on: () => ({ unsubscribe: () => undefined }) },
160
160
  } as never
161
161
  }
162
162
  >
@@ -75,7 +75,7 @@ const renderRow = ({
75
75
  // `useMutedUsers` reads the mutes reactively off `client.mutedUsersStore`.
76
76
  mutedUsersStore: new StateStore({ mutedUsers }),
77
77
  on: () => ({ unsubscribe: () => undefined }),
78
- userID: currentUserId,
78
+ userId: currentUserId,
79
79
  },
80
80
  } as never
81
81
  }
@@ -55,7 +55,7 @@ const t = ((
55
55
  const OWN_USER_ID = 'own-user';
56
56
  const channel = {
57
57
  cid: 'messaging:test',
58
- getClient: () => ({ userID: OWN_USER_ID }),
58
+ getClient: () => ({ userId: OWN_USER_ID }),
59
59
  } as unknown as Channel;
60
60
 
61
61
  const renderStatusText = () => {
@@ -60,7 +60,7 @@ export const ChannelMemberItem = ({
60
60
  if (!user) return null;
61
61
 
62
62
  const isLarge = size === 'lg';
63
- const isCurrentUser = !!client?.userID && user.id === client.userID;
63
+ const isCurrentUser = !!client?.userId && user.id === client.userId;
64
64
  const displayName = isCurrentUser ? t('common.you.label', 'You') : (user.name ?? user.id);
65
65
  const accessibilityLabel = composeAccessibilityLabel(
66
66
  displayName,
@@ -24,7 +24,7 @@ export const useChannelDetailsMemberStatusText = (channel: Channel): string => {
24
24
 
25
25
  return useMemo(() => {
26
26
  if (isDirect) {
27
- const ownUserId = channel.getClient().userID;
27
+ const ownUserId = channel.getClient().userId;
28
28
  const otherMember = Object.values(members).find((member) => member.user?.id !== ownUserId);
29
29
  return otherMember?.user?.online ? t('common.presence.online.label', 'Online') : '';
30
30
  }
@@ -36,7 +36,7 @@ export type ChannelListState = {
36
36
 
37
37
  export type ChannelListViewPropsWithContext = Omit<
38
38
  ChannelsContextValue,
39
- 'maxUnreadCount' | 'numberOfSkeletons' | 'onSelect'
39
+ 'maxUnreadCount' | 'numberOfSkeletons' | 'onSelect' | 'paginator'
40
40
  > &
41
41
  ChannelListState;
42
42
 
@@ -163,7 +163,6 @@ export const ChannelListView = (props: ChannelListViewProps) => {
163
163
  additionalFlatListProps,
164
164
  loadMoreThreshold,
165
165
  loadNextPage,
166
- paginator,
167
166
  refreshList,
168
167
  reloadList,
169
168
  setFlatListRef,
@@ -175,7 +174,6 @@ export const ChannelListView = (props: ChannelListViewProps) => {
175
174
  additionalFlatListProps,
176
175
  loadMoreThreshold,
177
176
  loadNextPage,
178
- paginator,
179
177
  refreshList,
180
178
  reloadList,
181
179
  setFlatListRef,
@@ -1084,6 +1084,50 @@ describe('ChannelList', () => {
1084
1084
  executeQuery.mockRestore();
1085
1085
  });
1086
1086
 
1087
+ it('keeps the footer loading indicator while a next page loads', async () => {
1088
+ useMockedApis(chatClient, [queryChannelsApi([testChannel1])]);
1089
+ const paginator = createPaginator({ paginatorOptions: { pageSize: 1 } });
1090
+ let loadNextPage: (() => Promise<void>) | undefined;
1091
+ const LoadNextPageProbe = () => {
1092
+ loadNextPage = useChannelsContext().loadNextPage;
1093
+ return null;
1094
+ };
1095
+
1096
+ render(
1097
+ <Chat client={chatClient}>
1098
+ <WithComponents overrides={{ ListHeaderComponent: LoadNextPageProbe }}>
1099
+ <ChannelList paginator={paginator} />
1100
+ </WithComponents>
1101
+ </Chat>,
1102
+ );
1103
+ await waitFor(() => expect(paginator.hasMoreTail).toBe(true));
1104
+
1105
+ useMockedApis(chatClient, [queryChannelsApi([testChannel2])]);
1106
+ const nextPage = new DeferredPromise<void>();
1107
+ const queryChannels = chatClient.queryChannels.bind(chatClient);
1108
+ jest.spyOn(chatClient, 'queryChannels').mockImplementation(async (...args) => {
1109
+ await nextPage.promise;
1110
+ return queryChannels(...args);
1111
+ });
1112
+ act(() => {
1113
+ loadNextPage?.();
1114
+ });
1115
+ expect(screen.getByTestId('channel-list-footer-loading-indicator')).toBeTruthy();
1116
+
1117
+ // FlatList asks again when the footer changes the content length
1118
+ await act(async () => {
1119
+ await loadNextPage?.();
1120
+ });
1121
+ expect(screen.getByTestId('channel-list-footer-loading-indicator')).toBeTruthy();
1122
+
1123
+ await act(async () => {
1124
+ nextPage.resolve();
1125
+ await nextPage.promise;
1126
+ });
1127
+ await waitFor(() => expect(paginator.items).toHaveLength(2));
1128
+ expect(screen.queryByTestId('channel-list-footer-loading-indicator')).toBeNull();
1129
+ });
1130
+
1087
1131
  it('shows the channels it already has when it mounts again, without a query', async () => {
1088
1132
  useMockedApis(chatClient, [queryChannelsApi([testChannel1])]);
1089
1133
 
@@ -41,7 +41,7 @@ export const usePaginatedChannels = ({ paginator }: { paginator: ChannelPaginato
41
41
 
42
42
  const queryChannels = useStableCallback(
43
43
  async (queryType: QueryType = 'loadChannels'): Promise<void> => {
44
- if (!client || !isMountedRef.current) {
44
+ if (!client || !isMountedRef.current || paginator.isLoading) {
45
45
  return;
46
46
  }
47
47