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

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 (527) 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/Channel.js +1 -0
  53. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  54. package/lib/commonjs/components/Channel/ChannelShell.js +8 -1
  55. package/lib/commonjs/components/Channel/ChannelShell.js.map +1 -1
  56. package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js +2 -1
  57. package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
  58. package/lib/commonjs/components/Channel/hooks/useJumpToLatestOnOpen.js +26 -0
  59. package/lib/commonjs/components/Channel/hooks/useJumpToLatestOnOpen.js.map +1 -0
  60. package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberItem.js +1 -1
  61. package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberItem.js.map +1 -1
  62. package/lib/commonjs/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js +1 -1
  63. package/lib/commonjs/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js.map +1 -1
  64. package/lib/commonjs/components/ChannelList/ChannelListView.js +0 -2
  65. package/lib/commonjs/components/ChannelList/ChannelListView.js.map +1 -1
  66. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js +2 -2
  67. package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  68. package/lib/commonjs/components/ChannelPreview/ChannelDetailsBottomSheet.js +1 -1
  69. package/lib/commonjs/components/ChannelPreview/ChannelDetailsBottomSheet.js.map +1 -1
  70. package/lib/commonjs/components/ChannelPreview/ChannelLastMessagePreview.js +1 -0
  71. package/lib/commonjs/components/ChannelPreview/ChannelLastMessagePreview.js.map +1 -1
  72. package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +2 -0
  73. package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
  74. package/lib/commonjs/components/ChannelPreview/ChannelPreviewTitle.js +1 -0
  75. package/lib/commonjs/components/ChannelPreview/ChannelPreviewTitle.js.map +1 -1
  76. package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js +1 -1
  77. package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
  78. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js +5 -5
  79. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
  80. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +1 -1
  81. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
  82. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js +1 -1
  83. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js.map +1 -1
  84. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +4 -1
  85. package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
  86. package/lib/commonjs/components/Chat/hooks/useConnectionLifecycle.js +6 -2
  87. package/lib/commonjs/components/Chat/hooks/useConnectionLifecycle.js.map +1 -1
  88. package/lib/commonjs/components/Indicators/LoadingErrorIndicator.js +5 -0
  89. package/lib/commonjs/components/Indicators/LoadingErrorIndicator.js.map +1 -1
  90. package/lib/commonjs/components/Message/Message.js +1 -1
  91. package/lib/commonjs/components/Message/Message.js.map +1 -1
  92. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js +4 -0
  93. package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  94. package/lib/commonjs/components/Message/MessageItemView/MessageWrapper.js +7 -4
  95. package/lib/commonjs/components/Message/MessageItemView/MessageWrapper.js.map +1 -1
  96. package/lib/commonjs/components/Message/hooks/useCreateMessageActionHandlers.js +3 -3
  97. package/lib/commonjs/components/Message/hooks/useCreateMessageActionHandlers.js.map +1 -1
  98. package/lib/commonjs/components/Message/hooks/useProcessReactions.js +2 -2
  99. package/lib/commonjs/components/Message/hooks/useProcessReactions.js.map +1 -1
  100. package/lib/commonjs/components/Message/hooks/useUserMuteActive.js +1 -1
  101. package/lib/commonjs/components/Message/hooks/useUserMuteActive.js.map +1 -1
  102. package/lib/commonjs/components/MessageInput/MessageComposer.js +2 -1
  103. package/lib/commonjs/components/MessageInput/MessageComposer.js.map +1 -1
  104. package/lib/commonjs/components/MessageList/MessageFlashList.js +2 -2
  105. package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
  106. package/lib/commonjs/components/MessageList/MessageList.js +2 -2
  107. package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
  108. package/lib/commonjs/components/MessageList/hooks/useOwnUnreadCount.js +1 -1
  109. package/lib/commonjs/components/MessageList/hooks/useOwnUnreadCount.js.map +1 -1
  110. package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js +1 -1
  111. package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
  112. package/lib/commonjs/components/MessageMenu/hooks/useMessageOwnReactions.js +2 -2
  113. package/lib/commonjs/components/MessageMenu/hooks/useMessageOwnReactions.js.map +1 -1
  114. package/lib/commonjs/components/Poll/components/PollAnswersList.js +1 -1
  115. package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
  116. package/lib/commonjs/components/Poll/components/PollButtons.js +1 -1
  117. package/lib/commonjs/components/Poll/components/PollButtons.js.map +1 -1
  118. package/lib/commonjs/components/Poll/hook/useIsPollCreatedByCurrentUser.js +1 -1
  119. package/lib/commonjs/components/Poll/hook/useIsPollCreatedByCurrentUser.js.map +1 -1
  120. package/lib/commonjs/components/Thread/Thread.js +1 -1
  121. package/lib/commonjs/components/Thread/Thread.js.map +1 -1
  122. package/lib/commonjs/components/ThreadList/ThreadListItem.js +1 -1
  123. package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -1
  124. package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js +9 -2
  125. package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js.map +1 -1
  126. package/lib/commonjs/components/index.js +36 -0
  127. package/lib/commonjs/components/index.js.map +1 -1
  128. package/lib/commonjs/contexts/componentsContext/defaultComponents.js +0 -2
  129. package/lib/commonjs/contexts/componentsContext/defaultComponents.js.map +1 -1
  130. package/lib/commonjs/contexts/liveLocationManagerContext/LiveLocationManagerContext.js +1 -1
  131. package/lib/commonjs/contexts/liveLocationManagerContext/LiveLocationManagerContext.js.map +1 -1
  132. package/lib/commonjs/hooks/actions/useChannelActionItems.js +1 -1
  133. package/lib/commonjs/hooks/actions/useChannelActionItems.js.map +1 -1
  134. package/lib/commonjs/hooks/actions/useChannelActions.js +2 -2
  135. package/lib/commonjs/hooks/actions/useChannelActions.js.map +1 -1
  136. package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js +1 -1
  137. package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js.map +1 -1
  138. package/lib/commonjs/hooks/index.js +11 -0
  139. package/lib/commonjs/hooks/index.js.map +1 -1
  140. package/lib/commonjs/hooks/messagePreview/useMessageDeliveryStatus.js +6 -6
  141. package/lib/commonjs/hooks/messagePreview/useMessageDeliveryStatus.js.map +1 -1
  142. package/lib/commonjs/hooks/useIsDirectChat.js +1 -1
  143. package/lib/commonjs/hooks/useIsDirectChat.js.map +1 -1
  144. package/lib/commonjs/i18n/runtimeDefaults.js +1 -0
  145. package/lib/commonjs/i18n/runtimeDefaults.js.map +1 -1
  146. package/lib/commonjs/store/apis/getChannelMessages.js +5 -3
  147. package/lib/commonjs/store/apis/getChannelMessages.js.map +1 -1
  148. package/lib/commonjs/store/apis/getChannels.js +6 -3
  149. package/lib/commonjs/store/apis/getChannels.js.map +1 -1
  150. package/lib/commonjs/store/apis/getChannelsForFilterSort.js +2 -1
  151. package/lib/commonjs/store/apis/getChannelsForFilterSort.js.map +1 -1
  152. package/lib/commonjs/store/apis/queries/selectMessagesForChannels.js +7 -5
  153. package/lib/commonjs/store/apis/queries/selectMessagesForChannels.js.map +1 -1
  154. package/lib/commonjs/utils/installNativeMultipartAdapter.js.map +1 -1
  155. package/lib/commonjs/utils/setupCommandUIMiddlewares.js +6 -3
  156. package/lib/commonjs/utils/setupCommandUIMiddlewares.js.map +1 -1
  157. package/lib/commonjs/version.json +1 -1
  158. package/lib/module/ai-components/README.md +50 -2
  159. package/lib/module/ai-components/index.js +24 -0
  160. package/lib/module/ai-components/index.js.map +1 -1
  161. package/lib/module/ai-components/message-composer/ComposerView.js +4 -2
  162. package/lib/module/ai-components/message-composer/ComposerView.js.map +1 -1
  163. package/lib/module/ai-components/message-composer/SpeechToTextButton.js +4 -1
  164. package/lib/module/ai-components/message-composer/SpeechToTextButton.js.map +1 -1
  165. package/lib/module/ai-components/native/AIDictation.js +6 -0
  166. package/lib/module/ai-components/native/AIDictation.js.map +1 -1
  167. package/lib/module/ai-components/store/dictation/store.js +4 -2
  168. package/lib/module/ai-components/store/dictation/store.js.map +1 -1
  169. package/lib/module/ai-components/store/dictation/useDictationState.js +4 -2
  170. package/lib/module/ai-components/store/dictation/useDictationState.js.map +1 -1
  171. package/lib/module/ai-components/store/index.js +22 -0
  172. package/lib/module/ai-components/store/index.js.map +1 -1
  173. package/lib/module/ai-components/theme.js +3 -0
  174. package/lib/module/ai-components/theme.js.map +1 -1
  175. package/lib/module/ai-components/transcription/useDictation.js +7 -3
  176. package/lib/module/ai-components/transcription/useDictation.js.map +1 -1
  177. package/lib/module/ai-components/transcription/useDictationAudioLevel.js +32 -0
  178. package/lib/module/ai-components/transcription/useDictationAudioLevel.js.map +1 -0
  179. package/lib/module/ai-components/voice-visualization/VoiceGlow.js +79 -0
  180. package/lib/module/ai-components/voice-visualization/VoiceGlow.js.map +1 -0
  181. package/lib/module/ai-components/voice-visualization/VoiceVisualizationView.js +147 -0
  182. package/lib/module/ai-components/voice-visualization/VoiceVisualizationView.js.map +1 -0
  183. package/lib/module/ai-components/voice-visualization/VoiceWave.js +58 -0
  184. package/lib/module/ai-components/voice-visualization/VoiceWave.js.map +1 -0
  185. package/lib/module/ai-components/voice-visualization/bounce.js +60 -0
  186. package/lib/module/ai-components/voice-visualization/bounce.js.map +1 -0
  187. package/lib/module/ai-components/voice-visualization/driver.js +208 -0
  188. package/lib/module/ai-components/voice-visualization/driver.js.map +1 -0
  189. package/lib/module/ai-components/voice-visualization/glowShader.js +218 -0
  190. package/lib/module/ai-components/voice-visualization/glowShader.js.map +1 -0
  191. package/lib/module/ai-components/voice-visualization/index.js +18 -0
  192. package/lib/module/ai-components/voice-visualization/index.js.map +1 -0
  193. package/lib/module/ai-components/voice-visualization/math.js +70 -0
  194. package/lib/module/ai-components/voice-visualization/math.js.map +1 -0
  195. package/lib/module/ai-components/voice-visualization/palette.js +47 -0
  196. package/lib/module/ai-components/voice-visualization/palette.js.map +1 -0
  197. package/lib/module/ai-components/voice-visualization/simplex.js +77 -0
  198. package/lib/module/ai-components/voice-visualization/simplex.js.map +1 -0
  199. package/lib/module/ai-components/voice-visualization/styles.js +33 -0
  200. package/lib/module/ai-components/voice-visualization/styles.js.map +1 -0
  201. package/lib/module/ai-components/voice-visualization/types.js +4 -0
  202. package/lib/module/ai-components/voice-visualization/types.js.map +1 -0
  203. package/lib/module/ai-components/voice-visualization/useReduceMotion.js +28 -0
  204. package/lib/module/ai-components/voice-visualization/useReduceMotion.js.map +1 -0
  205. package/lib/module/ai-components/voice-visualization/useVoiceVisualization.js +129 -0
  206. package/lib/module/ai-components/voice-visualization/useVoiceVisualization.js.map +1 -0
  207. package/lib/module/ai-components/voice-visualization/waveGeometry.js +124 -0
  208. package/lib/module/ai-components/voice-visualization/waveGeometry.js.map +1 -0
  209. package/lib/module/components/Channel/Channel.js +1 -0
  210. package/lib/module/components/Channel/Channel.js.map +1 -1
  211. package/lib/module/components/Channel/ChannelShell.js +8 -1
  212. package/lib/module/components/Channel/ChannelShell.js.map +1 -1
  213. package/lib/module/components/Channel/hooks/useChannelLifecycle.js +2 -1
  214. package/lib/module/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
  215. package/lib/module/components/Channel/hooks/useJumpToLatestOnOpen.js +26 -0
  216. package/lib/module/components/Channel/hooks/useJumpToLatestOnOpen.js.map +1 -0
  217. package/lib/module/components/ChannelDetails/components/members/ChannelMemberItem.js +1 -1
  218. package/lib/module/components/ChannelDetails/components/members/ChannelMemberItem.js.map +1 -1
  219. package/lib/module/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js +1 -1
  220. package/lib/module/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js.map +1 -1
  221. package/lib/module/components/ChannelList/ChannelListView.js +0 -2
  222. package/lib/module/components/ChannelList/ChannelListView.js.map +1 -1
  223. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js +2 -2
  224. package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
  225. package/lib/module/components/ChannelPreview/ChannelDetailsBottomSheet.js +1 -1
  226. package/lib/module/components/ChannelPreview/ChannelDetailsBottomSheet.js.map +1 -1
  227. package/lib/module/components/ChannelPreview/ChannelLastMessagePreview.js +1 -0
  228. package/lib/module/components/ChannelPreview/ChannelLastMessagePreview.js.map +1 -1
  229. package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +2 -0
  230. package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
  231. package/lib/module/components/ChannelPreview/ChannelPreviewTitle.js +1 -0
  232. package/lib/module/components/ChannelPreview/ChannelPreviewTitle.js.map +1 -1
  233. package/lib/module/components/ChannelPreview/ChannelPreviewView.js +1 -1
  234. package/lib/module/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
  235. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js +5 -5
  236. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
  237. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +1 -1
  238. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
  239. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js +1 -1
  240. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js.map +1 -1
  241. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +4 -1
  242. package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
  243. package/lib/module/components/Chat/hooks/useConnectionLifecycle.js +6 -2
  244. package/lib/module/components/Chat/hooks/useConnectionLifecycle.js.map +1 -1
  245. package/lib/module/components/Indicators/LoadingErrorIndicator.js +5 -0
  246. package/lib/module/components/Indicators/LoadingErrorIndicator.js.map +1 -1
  247. package/lib/module/components/Message/Message.js +1 -1
  248. package/lib/module/components/Message/Message.js.map +1 -1
  249. package/lib/module/components/Message/MessageItemView/MessageStatus.js +4 -0
  250. package/lib/module/components/Message/MessageItemView/MessageStatus.js.map +1 -1
  251. package/lib/module/components/Message/MessageItemView/MessageWrapper.js +7 -4
  252. package/lib/module/components/Message/MessageItemView/MessageWrapper.js.map +1 -1
  253. package/lib/module/components/Message/hooks/useCreateMessageActionHandlers.js +3 -3
  254. package/lib/module/components/Message/hooks/useCreateMessageActionHandlers.js.map +1 -1
  255. package/lib/module/components/Message/hooks/useProcessReactions.js +2 -2
  256. package/lib/module/components/Message/hooks/useProcessReactions.js.map +1 -1
  257. package/lib/module/components/Message/hooks/useUserMuteActive.js +1 -1
  258. package/lib/module/components/Message/hooks/useUserMuteActive.js.map +1 -1
  259. package/lib/module/components/MessageInput/MessageComposer.js +2 -1
  260. package/lib/module/components/MessageInput/MessageComposer.js.map +1 -1
  261. package/lib/module/components/MessageList/MessageFlashList.js +2 -2
  262. package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
  263. package/lib/module/components/MessageList/MessageList.js +2 -2
  264. package/lib/module/components/MessageList/MessageList.js.map +1 -1
  265. package/lib/module/components/MessageList/hooks/useOwnUnreadCount.js +1 -1
  266. package/lib/module/components/MessageList/hooks/useOwnUnreadCount.js.map +1 -1
  267. package/lib/module/components/MessageMenu/MessageUserReactionsItem.js +1 -1
  268. package/lib/module/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
  269. package/lib/module/components/MessageMenu/hooks/useMessageOwnReactions.js +2 -2
  270. package/lib/module/components/MessageMenu/hooks/useMessageOwnReactions.js.map +1 -1
  271. package/lib/module/components/Poll/components/PollAnswersList.js +1 -1
  272. package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
  273. package/lib/module/components/Poll/components/PollButtons.js +1 -1
  274. package/lib/module/components/Poll/components/PollButtons.js.map +1 -1
  275. package/lib/module/components/Poll/hook/useIsPollCreatedByCurrentUser.js +1 -1
  276. package/lib/module/components/Poll/hook/useIsPollCreatedByCurrentUser.js.map +1 -1
  277. package/lib/module/components/Thread/Thread.js +1 -1
  278. package/lib/module/components/Thread/Thread.js.map +1 -1
  279. package/lib/module/components/ThreadList/ThreadListItem.js +1 -1
  280. package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -1
  281. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js +9 -2
  282. package/lib/module/components/ThreadList/ThreadListUnreadBanner.js.map +1 -1
  283. package/lib/module/components/index.js +36 -0
  284. package/lib/module/components/index.js.map +1 -1
  285. package/lib/module/contexts/componentsContext/defaultComponents.js +0 -2
  286. package/lib/module/contexts/componentsContext/defaultComponents.js.map +1 -1
  287. package/lib/module/contexts/liveLocationManagerContext/LiveLocationManagerContext.js +1 -1
  288. package/lib/module/contexts/liveLocationManagerContext/LiveLocationManagerContext.js.map +1 -1
  289. package/lib/module/hooks/actions/useChannelActionItems.js +1 -1
  290. package/lib/module/hooks/actions/useChannelActionItems.js.map +1 -1
  291. package/lib/module/hooks/actions/useChannelActions.js +2 -2
  292. package/lib/module/hooks/actions/useChannelActions.js.map +1 -1
  293. package/lib/module/hooks/actions/useChannelMemberActionItems.js +1 -1
  294. package/lib/module/hooks/actions/useChannelMemberActionItems.js.map +1 -1
  295. package/lib/module/hooks/index.js +11 -0
  296. package/lib/module/hooks/index.js.map +1 -1
  297. package/lib/module/hooks/messagePreview/useMessageDeliveryStatus.js +6 -6
  298. package/lib/module/hooks/messagePreview/useMessageDeliveryStatus.js.map +1 -1
  299. package/lib/module/hooks/useIsDirectChat.js +1 -1
  300. package/lib/module/hooks/useIsDirectChat.js.map +1 -1
  301. package/lib/module/i18n/runtimeDefaults.js +1 -0
  302. package/lib/module/i18n/runtimeDefaults.js.map +1 -1
  303. package/lib/module/store/apis/getChannelMessages.js +5 -3
  304. package/lib/module/store/apis/getChannelMessages.js.map +1 -1
  305. package/lib/module/store/apis/getChannels.js +6 -3
  306. package/lib/module/store/apis/getChannels.js.map +1 -1
  307. package/lib/module/store/apis/getChannelsForFilterSort.js +2 -1
  308. package/lib/module/store/apis/getChannelsForFilterSort.js.map +1 -1
  309. package/lib/module/store/apis/queries/selectMessagesForChannels.js +7 -5
  310. package/lib/module/store/apis/queries/selectMessagesForChannels.js.map +1 -1
  311. package/lib/module/utils/installNativeMultipartAdapter.js.map +1 -1
  312. package/lib/module/utils/setupCommandUIMiddlewares.js +6 -3
  313. package/lib/module/utils/setupCommandUIMiddlewares.js.map +1 -1
  314. package/lib/module/version.json +1 -1
  315. package/lib/typescript/ai-components/index.d.ts +3 -1
  316. package/lib/typescript/ai-components/index.d.ts.map +1 -1
  317. package/lib/typescript/ai-components/message-composer/ComposerView.d.ts.map +1 -1
  318. package/lib/typescript/ai-components/native/AIDictation.d.ts +6 -0
  319. package/lib/typescript/ai-components/native/AIDictation.d.ts.map +1 -1
  320. package/lib/typescript/ai-components/store/dictation/store.d.ts +7 -0
  321. package/lib/typescript/ai-components/store/dictation/store.d.ts.map +1 -1
  322. package/lib/typescript/ai-components/store/dictation/useDictationState.d.ts +1 -0
  323. package/lib/typescript/ai-components/store/dictation/useDictationState.d.ts.map +1 -1
  324. package/lib/typescript/ai-components/store/index.d.ts +2 -0
  325. package/lib/typescript/ai-components/store/index.d.ts.map +1 -1
  326. package/lib/typescript/ai-components/theme.d.ts +13 -0
  327. package/lib/typescript/ai-components/theme.d.ts.map +1 -1
  328. package/lib/typescript/ai-components/transcription/useDictation.d.ts +9 -1
  329. package/lib/typescript/ai-components/transcription/useDictation.d.ts.map +1 -1
  330. package/lib/typescript/ai-components/transcription/useDictationAudioLevel.d.ts +11 -0
  331. package/lib/typescript/ai-components/transcription/useDictationAudioLevel.d.ts.map +1 -0
  332. package/lib/typescript/ai-components/voice-visualization/VoiceGlow.d.ts +13 -0
  333. package/lib/typescript/ai-components/voice-visualization/VoiceGlow.d.ts.map +1 -0
  334. package/lib/typescript/ai-components/voice-visualization/VoiceVisualizationView.d.ts +26 -0
  335. package/lib/typescript/ai-components/voice-visualization/VoiceVisualizationView.d.ts.map +1 -0
  336. package/lib/typescript/ai-components/voice-visualization/VoiceWave.d.ts +13 -0
  337. package/lib/typescript/ai-components/voice-visualization/VoiceWave.d.ts.map +1 -0
  338. package/lib/typescript/ai-components/voice-visualization/bounce.d.ts +20 -0
  339. package/lib/typescript/ai-components/voice-visualization/bounce.d.ts.map +1 -0
  340. package/lib/typescript/ai-components/voice-visualization/driver.d.ts +96 -0
  341. package/lib/typescript/ai-components/voice-visualization/driver.d.ts.map +1 -0
  342. package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts +7 -0
  343. package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts.map +1 -0
  344. package/lib/typescript/ai-components/voice-visualization/index.d.ts +4 -0
  345. package/lib/typescript/ai-components/voice-visualization/index.d.ts.map +1 -0
  346. package/lib/typescript/ai-components/voice-visualization/math.d.ts +26 -0
  347. package/lib/typescript/ai-components/voice-visualization/math.d.ts.map +1 -0
  348. package/lib/typescript/ai-components/voice-visualization/palette.d.ts +18 -0
  349. package/lib/typescript/ai-components/voice-visualization/palette.d.ts.map +1 -0
  350. package/lib/typescript/ai-components/voice-visualization/simplex.d.ts +11 -0
  351. package/lib/typescript/ai-components/voice-visualization/simplex.d.ts.map +1 -0
  352. package/lib/typescript/ai-components/voice-visualization/styles.d.ts +24 -0
  353. package/lib/typescript/ai-components/voice-visualization/styles.d.ts.map +1 -0
  354. package/lib/typescript/ai-components/voice-visualization/types.d.ts +45 -0
  355. package/lib/typescript/ai-components/voice-visualization/types.d.ts.map +1 -0
  356. package/lib/typescript/ai-components/voice-visualization/useReduceMotion.d.ts +10 -0
  357. package/lib/typescript/ai-components/voice-visualization/useReduceMotion.d.ts.map +1 -0
  358. package/lib/typescript/ai-components/voice-visualization/useVoiceVisualization.d.ts +18 -0
  359. package/lib/typescript/ai-components/voice-visualization/useVoiceVisualization.d.ts.map +1 -0
  360. package/lib/typescript/ai-components/voice-visualization/waveGeometry.d.ts +25 -0
  361. package/lib/typescript/ai-components/voice-visualization/waveGeometry.d.ts.map +1 -0
  362. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  363. package/lib/typescript/components/Channel/ChannelShell.d.ts +2 -0
  364. package/lib/typescript/components/Channel/ChannelShell.d.ts.map +1 -1
  365. package/lib/typescript/components/Channel/hooks/useChannelLifecycle.d.ts.map +1 -1
  366. package/lib/typescript/components/Channel/hooks/useJumpToLatestOnOpen.d.ts +15 -0
  367. package/lib/typescript/components/Channel/hooks/useJumpToLatestOnOpen.d.ts.map +1 -0
  368. package/lib/typescript/components/ChannelList/ChannelListView.d.ts +1 -1
  369. package/lib/typescript/components/ChannelList/ChannelListView.d.ts.map +1 -1
  370. package/lib/typescript/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.d.ts.map +1 -1
  371. package/lib/typescript/components/ChannelPreview/hooks/useChannelPreviewData.d.ts.map +1 -1
  372. package/lib/typescript/components/Chat/hooks/useConnectionLifecycle.d.ts +5 -4
  373. package/lib/typescript/components/Chat/hooks/useConnectionLifecycle.d.ts.map +1 -1
  374. package/lib/typescript/components/Indicators/LoadingErrorIndicator.d.ts +1 -1
  375. package/lib/typescript/components/Indicators/LoadingErrorIndicator.d.ts.map +1 -1
  376. package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts.map +1 -1
  377. package/lib/typescript/components/Message/MessageItemView/MessageWrapper.d.ts.map +1 -1
  378. package/lib/typescript/components/MessageInput/MessageComposer.d.ts.map +1 -1
  379. package/lib/typescript/components/ThreadList/ThreadListUnreadBanner.d.ts.map +1 -1
  380. package/lib/typescript/components/index.d.ts +3 -0
  381. package/lib/typescript/components/index.d.ts.map +1 -1
  382. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +0 -1
  383. package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts.map +1 -1
  384. package/lib/typescript/hooks/index.d.ts +1 -0
  385. package/lib/typescript/hooks/index.d.ts.map +1 -1
  386. package/lib/typescript/hooks/messagePreview/useMessageDeliveryStatus.d.ts +3 -0
  387. package/lib/typescript/hooks/messagePreview/useMessageDeliveryStatus.d.ts.map +1 -1
  388. package/lib/typescript/i18n/keys.d.ts +3 -1
  389. package/lib/typescript/i18n/keys.d.ts.map +1 -1
  390. package/lib/typescript/i18n/runtimeDefaults.d.ts +1 -0
  391. package/lib/typescript/i18n/runtimeDefaults.d.ts.map +1 -1
  392. package/lib/typescript/store/apis/getChannelMessages.d.ts +2 -1
  393. package/lib/typescript/store/apis/getChannelMessages.d.ts.map +1 -1
  394. package/lib/typescript/store/apis/getChannels.d.ts +3 -1
  395. package/lib/typescript/store/apis/getChannels.d.ts.map +1 -1
  396. package/lib/typescript/store/apis/getChannelsForFilterSort.d.ts.map +1 -1
  397. package/lib/typescript/store/apis/queries/selectMessagesForChannels.d.ts +3 -1
  398. package/lib/typescript/store/apis/queries/selectMessagesForChannels.d.ts.map +1 -1
  399. package/lib/typescript/utils/installNativeMultipartAdapter.d.ts +5 -3
  400. package/lib/typescript/utils/installNativeMultipartAdapter.d.ts.map +1 -1
  401. package/lib/typescript/utils/setupCommandUIMiddlewares.d.ts.map +1 -1
  402. package/package.json +4 -4
  403. package/src/__tests__/offline-support/offline-feature.tsx +7 -7
  404. package/src/__tests__/offline-support/optimistic-update.tsx +5 -5
  405. package/src/ai-components/README.md +50 -2
  406. package/src/ai-components/__tests__/VoiceVisualization.test.tsx +208 -0
  407. package/src/ai-components/__tests__/dictation.test.tsx +58 -1
  408. package/src/ai-components/__tests__/voiceDriver.test.ts +317 -0
  409. package/src/ai-components/__tests__/voicePalette.test.ts +124 -0
  410. package/src/ai-components/index.ts +3 -1
  411. package/src/ai-components/message-composer/ComposerView.tsx +6 -3
  412. package/src/ai-components/message-composer/SpeechToTextButton.tsx +5 -2
  413. package/src/ai-components/native/AIDictation.ts +11 -0
  414. package/src/ai-components/store/dictation/store.ts +9 -0
  415. package/src/ai-components/store/dictation/useDictationState.ts +2 -1
  416. package/src/ai-components/store/index.ts +2 -0
  417. package/src/ai-components/theme.ts +16 -0
  418. package/src/ai-components/transcription/useDictation.ts +16 -3
  419. package/src/ai-components/transcription/useDictationAudioLevel.ts +38 -0
  420. package/src/ai-components/voice-visualization/VoiceGlow.tsx +79 -0
  421. package/src/ai-components/voice-visualization/VoiceVisualizationView.tsx +179 -0
  422. package/src/ai-components/voice-visualization/VoiceWave.tsx +67 -0
  423. package/src/ai-components/voice-visualization/bounce.ts +61 -0
  424. package/src/ai-components/voice-visualization/driver.ts +240 -0
  425. package/src/ai-components/voice-visualization/glowShader.ts +227 -0
  426. package/src/ai-components/voice-visualization/index.ts +3 -0
  427. package/src/ai-components/voice-visualization/math.ts +73 -0
  428. package/src/ai-components/voice-visualization/palette.ts +59 -0
  429. package/src/ai-components/voice-visualization/simplex.ts +92 -0
  430. package/src/ai-components/voice-visualization/styles.ts +42 -0
  431. package/src/ai-components/voice-visualization/types.ts +44 -0
  432. package/src/ai-components/voice-visualization/useReduceMotion.ts +34 -0
  433. package/src/ai-components/voice-visualization/useVoiceVisualization.ts +153 -0
  434. package/src/ai-components/voice-visualization/waveGeometry.ts +137 -0
  435. package/src/components/Channel/Channel.tsx +1 -0
  436. package/src/components/Channel/ChannelShell.tsx +12 -0
  437. package/src/components/Channel/__tests__/Channel.test.tsx +146 -5
  438. package/src/components/Channel/hooks/useChannelLifecycle.ts +4 -1
  439. package/src/components/Channel/hooks/useJumpToLatestOnOpen.ts +42 -0
  440. package/src/components/ChannelDetails/__tests__/ChannelDetails.test.tsx +1 -1
  441. package/src/components/ChannelDetails/__tests__/ChannelDetailsActionsSection.test.tsx +1 -1
  442. package/src/components/ChannelDetails/__tests__/ChannelDetailsEditButton.test.tsx +1 -1
  443. package/src/components/ChannelDetails/__tests__/ChannelDetailsMemberSection.test.tsx +1 -1
  444. package/src/components/ChannelDetails/__tests__/ChannelDetailsNavHeader.test.tsx +1 -1
  445. package/src/components/ChannelDetails/__tests__/ChannelDetailsProfile.test.tsx +2 -2
  446. package/src/components/ChannelDetails/__tests__/ChannelEditDetailsForm.test.tsx +1 -1
  447. package/src/components/ChannelDetails/__tests__/ChannelEditDetailsFormContent.test.tsx +2 -2
  448. package/src/components/ChannelDetails/__tests__/ChannelEditName.test.tsx +1 -1
  449. package/src/components/ChannelDetails/__tests__/members/ChannelAddMembersForm.test.tsx +1 -1
  450. package/src/components/ChannelDetails/__tests__/members/ChannelMemberActionsSheet.test.tsx +2 -2
  451. package/src/components/ChannelDetails/__tests__/members/ChannelMemberItem.test.tsx +1 -1
  452. package/src/components/ChannelDetails/__tests__/useChannelDetailsMemberStatusText.test.tsx +1 -1
  453. package/src/components/ChannelDetails/components/members/ChannelMemberItem.tsx +1 -1
  454. package/src/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.ts +1 -1
  455. package/src/components/ChannelList/ChannelListView.tsx +1 -3
  456. package/src/components/ChannelList/__tests__/ChannelList.test.tsx +72 -0
  457. package/src/components/ChannelList/hooks/usePaginatedChannels.ts +2 -2
  458. package/src/components/ChannelPreview/ChannelDetailsBottomSheet.tsx +1 -1
  459. package/src/components/ChannelPreview/ChannelLastMessagePreview.tsx +1 -1
  460. package/src/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.tsx +8 -2
  461. package/src/components/ChannelPreview/ChannelPreviewTitle.tsx +1 -1
  462. package/src/components/ChannelPreview/ChannelPreviewView.tsx +1 -1
  463. package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx +4 -4
  464. package/src/components/ChannelPreview/__tests__/ChannelPreviewView.test.tsx +40 -0
  465. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewData.test.tsx +51 -0
  466. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayName.test.tsx +1 -1
  467. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayPresence.test.tsx +1 -1
  468. package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewPollLabel.test.tsx +87 -0
  469. package/src/components/ChannelPreview/hooks/useChannelPreviewData.ts +3 -4
  470. package/src/components/ChannelPreview/hooks/useChannelPreviewDisplayName.ts +1 -1
  471. package/src/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.ts +1 -1
  472. package/src/components/ChannelPreview/hooks/useChannelPreviewPollLabel.ts +1 -1
  473. package/src/components/Chat/__tests__/Chat.test.tsx +31 -1
  474. package/src/components/Chat/hooks/useConnectionLifecycle.ts +12 -6
  475. package/src/components/Indicators/LoadingErrorIndicator.tsx +8 -1
  476. package/src/components/Message/Message.tsx +1 -1
  477. package/src/components/Message/MessageItemView/MessageStatus.tsx +16 -2
  478. package/src/components/Message/MessageItemView/MessageWrapper.tsx +14 -4
  479. package/src/components/Message/MessageItemView/__tests__/MessageStatus.test.tsx +21 -0
  480. package/src/components/Message/hooks/__tests__/useCreateMessageActionHandlers.test.tsx +1 -1
  481. package/src/components/Message/hooks/__tests__/useUserMuteActive.test.tsx +1 -1
  482. package/src/components/Message/hooks/useCreateMessageActionHandlers.ts +3 -3
  483. package/src/components/Message/hooks/useProcessReactions.ts +2 -2
  484. package/src/components/Message/hooks/useUserMuteActive.ts +1 -1
  485. package/src/components/MessageInput/MessageComposer.tsx +4 -2
  486. package/src/components/MessageList/MessageFlashList.tsx +2 -2
  487. package/src/components/MessageList/MessageList.tsx +2 -2
  488. package/src/components/MessageList/__tests__/MessageList.test.tsx +115 -14
  489. package/src/components/MessageList/__tests__/ScrollToBottomButton.test.tsx +3 -3
  490. package/src/components/MessageList/hooks/useOwnUnreadCount.ts +1 -1
  491. package/src/components/MessageMenu/MessageUserReactionsItem.tsx +1 -1
  492. package/src/components/MessageMenu/hooks/useMessageOwnReactions.ts +2 -2
  493. package/src/components/Poll/components/PollAnswersList.tsx +1 -1
  494. package/src/components/Poll/components/PollButtons.tsx +1 -1
  495. package/src/components/Poll/hook/useIsPollCreatedByCurrentUser.ts +1 -1
  496. package/src/components/Thread/Thread.tsx +1 -1
  497. package/src/components/Thread/__tests__/Thread.test.tsx +89 -2
  498. package/src/components/ThreadList/ThreadListItem.tsx +1 -1
  499. package/src/components/ThreadList/ThreadListUnreadBanner.tsx +6 -2
  500. package/src/components/ThreadList/__tests__/ThreadList.test.tsx +21 -0
  501. package/src/components/index.ts +3 -0
  502. package/src/contexts/componentsContext/defaultComponents.ts +0 -2
  503. package/src/contexts/liveLocationManagerContext/LiveLocationManagerContext.tsx +1 -1
  504. package/src/hooks/__tests__/useChannelMuteActive.test.tsx +1 -1
  505. package/src/hooks/__tests__/useIsDirectChat.test.tsx +1 -1
  506. package/src/hooks/actions/__tests__/useChannelActionItems.test.tsx +1 -1
  507. package/src/hooks/actions/__tests__/useChannelActions.test.tsx +1 -1
  508. package/src/hooks/actions/__tests__/useChannelMemberActionItems.test.tsx +6 -6
  509. package/src/hooks/actions/useChannelActionItems.tsx +1 -1
  510. package/src/hooks/actions/useChannelActions.ts +2 -2
  511. package/src/hooks/actions/useChannelMemberActionItems.tsx +1 -1
  512. package/src/hooks/index.ts +1 -0
  513. package/src/hooks/messagePreview/__tests__/useMessageDeliveryStatus.test.tsx +57 -32
  514. package/src/hooks/messagePreview/useMessageDeliveryStatus.ts +13 -7
  515. package/src/hooks/useIsDirectChat.ts +1 -1
  516. package/src/i18n/__tests__/catalog.fixture.json +2 -0
  517. package/src/i18n/keys.ts +3 -0
  518. package/src/i18n/runtimeDefaults.ts +1 -0
  519. package/src/store/__tests__/selectMessagesForChannels.test.ts +34 -12
  520. package/src/store/apis/getChannelMessages.ts +4 -1
  521. package/src/store/apis/getChannels.ts +5 -1
  522. package/src/store/apis/getChannelsForFilterSort.ts +1 -0
  523. package/src/store/apis/queries/selectMessagesForChannels.ts +7 -2
  524. package/src/utils/__tests__/setupCommandUIMiddlewares.test.ts +49 -0
  525. package/src/utils/installNativeMultipartAdapter.ts +5 -3
  526. package/src/utils/setupCommandUIMiddlewares.ts +3 -0
  527. 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);
@@ -104,6 +104,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
104
104
  {...shellProps}
105
105
  channel={channel}
106
106
  hasPendingInitialTargetLoad={hasPendingInitialTargetLoad}
107
+ initializeOnMount={initializeOnMount}
107
108
  >
108
109
  {children}
109
110
  </ChannelShell>
@@ -15,6 +15,7 @@ import { useCreateChannelContext } from './hooks/useCreateChannelContext';
15
15
  import { useCreateInputMessageInputContext } from './hooks/useCreateInputMessageInputContext';
16
16
  import { useCreateMessagesContext } from './hooks/useCreateMessagesContext';
17
17
  import { useCreateOwnCapabilitiesContext } from './hooks/useCreateOwnCapabilitiesContext';
18
+ import { useJumpToLatestOnOpen } from './hooks/useJumpToLatestOnOpen';
18
19
 
19
20
  import {
20
21
  AttachmentPickerContextValue,
@@ -208,6 +209,8 @@ export type ChannelConfigProps = Partial<
208
209
  type ChannelShellProps = ChannelConfigProps & {
209
210
  channel: Channel;
210
211
  hasPendingInitialTargetLoad: () => boolean;
212
+ /** `<Channel>`'s `initializeOnMount`: when false, the app places the list's pages itself. */
213
+ initializeOnMount?: boolean;
211
214
  /** Set by `<Thread>`: the list and composer below show this thread's replies. */
212
215
  thread?: Thread;
213
216
  };
@@ -228,6 +231,13 @@ const channelStatusSelector = (state: ChannelDataState & ChannelLifecycleState)
228
231
  pendingDisposal: state.pendingDisposal,
229
232
  });
230
233
 
234
+ // Renders nothing. It sits under the providers below so its hook can read the
235
+ // channel and thread context this component provides.
236
+ const JumpToLatestOnOpen = () => {
237
+ useJumpToLatestOnOpen();
238
+ return null;
239
+ };
240
+
231
241
  /**
232
242
  * The providers a message list and composer need, shared by `<Channel>` and `<Thread>`, which each
233
243
  * own their lifecycle (watching, loading, marking read) and render this around their children.
@@ -288,6 +298,7 @@ export const ChannelShell = (props: PropsWithChildren<ChannelShellProps>) => {
288
298
  hasFilePicker = isDocumentPickerAvailable(),
289
299
  hasImagePicker = isImagePickerAvailable() || isImageMediaLibraryAvailable(),
290
300
  hasPendingInitialTargetLoad,
301
+ initializeOnMount = true,
291
302
  allowDateSeparatorForSystemMessages = false,
292
303
  hideDateSeparators = false,
293
304
  hideStickyDateHeader = false,
@@ -508,6 +519,7 @@ export const ChannelShell = (props: PropsWithChildren<ChannelShellProps>) => {
508
519
  <MessageInputProvider value={inputMessageInputContext}>
509
520
  <AudioPlayerProvider value={audioPlayerContext}>
510
521
  <NotificationAnnouncer />
522
+ {initializeOnMount ? <JumpToLatestOnOpen /> : null}
511
523
  <View style={{ height: '100%' }}>{children}</View>
512
524
  {shouldRenderAttachmentPicker ? <AttachmentPicker /> : null}
513
525
  </AudioPlayerProvider>
@@ -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,147 @@ 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
+
799
+ describe('reopening a channel left on an older page', () => {
800
+ const OLDER_PAGE_START = new Date('2020-01-01T00:00:00.000Z').getTime();
801
+
802
+ // A watched channel whose newest messages are loaded, but whose list shows an older page, as it
803
+ // does after a jump to a quoted, pinned or searched message.
804
+ const setUpChannelOnOlderPage = async () => {
805
+ const newestMessages = Array.from({ length: 10 }, (_, i) =>
806
+ generateMessage({ id: `newest-${i}` }),
807
+ );
808
+ const mockedChannel = generateChannelResponse({ messages: newestMessages });
809
+ useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
810
+ const channel = chatClient.channel('messaging', mockedChannel.channel.id);
811
+ await channel.watch();
812
+
813
+ const olderMessages = Array.from({ length: 5 }, (_, i) =>
814
+ generateMessage({ id: `older-${i}`, timestamp: OLDER_PAGE_START + i * 1000 }),
815
+ );
816
+ channel.messagePaginator.ingestPage({
817
+ page: olderMessages,
818
+ policy: 'strict-overlap-only',
819
+ setActive: true,
820
+ });
821
+ expect(channel.messagePaginator.isActiveIntervalAtHead).toBe(false);
822
+
823
+ return channel;
824
+ };
825
+
826
+ it('shows the newest messages again', async () => {
827
+ const channel = await setUpChannelOnOlderPage();
828
+
829
+ renderComponent({ channel, markReadOnMount: false });
830
+
831
+ await waitFor(() => expect(channel.messagePaginator.isActiveIntervalAtHead).toBe(true));
832
+ expect(channel.messagePaginator.items?.map((message) => message.id)).toContain('newest-9');
833
+ });
834
+
835
+ it('keeps a jump the integrator already made to a loaded message', async () => {
836
+ const channel = await setUpChannelOnOlderPage();
837
+ // A deep link from the screen: the target is loaded, so the jump lands at once.
838
+ await channel.messagePaginator.jumpToMessage('older-2', { focusReason: 'jump-to-message' });
839
+ const seedSpy = jest.spyOn(channel.messagePaginator, 'seedUnreadSnapshot');
840
+
841
+ renderComponent({ channel, markReadOnMount: false });
842
+
843
+ await waitFor(() => expect(seedSpy).toHaveBeenCalled());
844
+ expect(channel.messagePaginator.isActiveIntervalAtHead).toBe(false);
845
+ expect(channel.messagePaginator.messageFocusSignal.getLatestValue().signal?.messageId).toBe(
846
+ 'older-2',
847
+ );
848
+ });
849
+
850
+ it('leaves a jump that is still in flight alone', async () => {
851
+ const channel = await setUpChannelOnOlderPage();
852
+ // A deep link to a message that is not loaded: its query is in flight.
853
+ jest.spyOn(channel.messagePaginator, 'isLoading', 'get').mockReturnValue(true);
854
+ const jumpToTheLatestMessageSpy = jest.spyOn(
855
+ channel.messagePaginator,
856
+ 'jumpToTheLatestMessage',
857
+ );
858
+ const seedSpy = jest.spyOn(channel.messagePaginator, 'seedUnreadSnapshot');
859
+
860
+ renderComponent({ channel, markReadOnMount: false });
861
+
862
+ await waitFor(() => expect(seedSpy).toHaveBeenCalled());
863
+ expect(jumpToTheLatestMessageSpy).not.toHaveBeenCalled();
864
+ });
865
+
866
+ it('leaves the list alone when the app places its pages itself', async () => {
867
+ const channel = await setUpChannelOnOlderPage();
868
+ const jumpToTheLatestMessageSpy = jest.spyOn(
869
+ channel.messagePaginator,
870
+ 'jumpToTheLatestMessage',
871
+ );
872
+
873
+ renderComponent({ channel, initializeOnMount: false, markReadOnMount: false });
874
+
875
+ expect(jumpToTheLatestMessageSpy).not.toHaveBeenCalled();
876
+ expect(channel.messagePaginator.isActiveIntervalAtHead).toBe(false);
877
+ });
878
+
879
+ it('leaves the jump to the first unread message to the initial load', async () => {
880
+ const channel = await setUpChannelOnOlderPage();
881
+ jest.spyOn(channel, 'countUnread').mockImplementation(() => 3);
882
+ const jumpToTheFirstUnreadMessageSpy = jest
883
+ .spyOn(channel.messagePaginator, 'jumpToTheFirstUnreadMessage')
884
+ .mockResolvedValue(undefined as never);
885
+ const jumpToTheLatestMessageSpy = jest.spyOn(
886
+ channel.messagePaginator,
887
+ 'jumpToTheLatestMessage',
888
+ );
889
+
890
+ renderComponent({
891
+ channel,
892
+ initialScrollToFirstUnreadMessage: true,
893
+ markReadOnMount: false,
894
+ });
895
+
896
+ await waitFor(() => expect(jumpToTheFirstUnreadMessageSpy).toHaveBeenCalledTimes(1));
897
+ expect(jumpToTheLatestMessageSpy).not.toHaveBeenCalled();
898
+ });
899
+ });
900
+
760
901
  describe('message page size', () => {
761
902
  const mountChannel = async () => {
762
903
  const mockedChannel = generateChannelResponse({ messages: [] });
@@ -979,7 +1120,7 @@ describe('Channel initial load useEffect', () => {
979
1120
  await channel.watch();
980
1121
 
981
1122
  // Call through to the real reload so the resync actually reconciles (and we can assert it ran).
982
- const reloadSpy = jest.spyOn(channel, 'reload');
1123
+ const seedSpy = jest.spyOn(channel, 'reload');
983
1124
 
984
1125
  renderComponent({ channel });
985
1126
 
@@ -1007,7 +1148,7 @@ describe('Channel initial load useEffect', () => {
1007
1148
  // The reload re-watches and reconciles, but preserves the failed (locally-unsent) messages —
1008
1149
  // the 10 originals + 10 failed remain.
1009
1150
  await waitFor(() => {
1010
- expect(reloadSpy).toHaveBeenCalled();
1151
+ expect(seedSpy).toHaveBeenCalled();
1011
1152
  expect(channel.messagePaginator.anchoredHeadItems.length).toBe(20);
1012
1153
  });
1013
1154
  });
@@ -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 {
@@ -0,0 +1,42 @@
1
+ import { useLayoutEffect } from 'react';
2
+
3
+ import { useChannelContext } from '../../../contexts/channelContext/ChannelContext';
4
+ import { useThreadContext } from '../../../contexts/threadContext/ThreadContext';
5
+ import { useMessagePaginator } from '../../../hooks/useMessagePaginator';
6
+
7
+ /**
8
+ * Shows the newest messages when a channel or thread is opened on an older page. Its paginator
9
+ * outlives the screen, so it keeps the page it was left on, such as one after a jump to a quoted,
10
+ * pinned or searched message. Runs before the first paint, so the old page never flashes.
11
+ *
12
+ * Stays out of the way of a jump that already landed or is in flight (a deep link from the screen)
13
+ * and of the jump to the first unread message. Runs once per channel or thread: its dependencies
14
+ * are stable, so nothing that changes while it is open moves the list.
15
+ *
16
+ * `<Channel>` and `<Thread>` already run it, unless `<Channel initializeOnMount={false}>`. Call it
17
+ * from a component inside `<Channel>` or `<Thread>` only when the app opted out that way and still
18
+ * wants the newest messages shown on open.
19
+ */
20
+ export const useJumpToLatestOnOpen = () => {
21
+ const { channel, hasPendingInitialTargetLoad } = useChannelContext();
22
+ const { thread } = useThreadContext();
23
+ const paginator = useMessagePaginator();
24
+
25
+ useLayoutEffect(() => {
26
+ // A channel that is not loaded yet, or a draft, gets its first page from `<Channel>` itself.
27
+ const isChannelLoading = !thread && (!channel.initialized || channel.isProvisional);
28
+ if (
29
+ isChannelLoading ||
30
+ !paginator.items?.length ||
31
+ paginator.isActiveIntervalAtHead ||
32
+ paginator.isLoading ||
33
+ hasPendingInitialTargetLoad?.()
34
+ ) {
35
+ return;
36
+ }
37
+
38
+ paginator
39
+ .jumpToTheLatestMessage()
40
+ .catch((error: unknown) => console.warn('Jumping to the latest message failed:', error));
41
+ }, [channel, hasPendingInitialTargetLoad, paginator, thread]);
42
+ };
@@ -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 }}>