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.
- package/lib/commonjs/ai-components/README.md +50 -2
- package/lib/commonjs/ai-components/index.js +24 -0
- package/lib/commonjs/ai-components/index.js.map +1 -1
- package/lib/commonjs/ai-components/message-composer/ComposerView.js +4 -2
- package/lib/commonjs/ai-components/message-composer/ComposerView.js.map +1 -1
- package/lib/commonjs/ai-components/message-composer/SpeechToTextButton.js +4 -1
- package/lib/commonjs/ai-components/message-composer/SpeechToTextButton.js.map +1 -1
- package/lib/commonjs/ai-components/native/AIDictation.js +6 -0
- package/lib/commonjs/ai-components/native/AIDictation.js.map +1 -1
- package/lib/commonjs/ai-components/store/dictation/store.js +4 -2
- package/lib/commonjs/ai-components/store/dictation/store.js.map +1 -1
- package/lib/commonjs/ai-components/store/dictation/useDictationState.js +4 -2
- package/lib/commonjs/ai-components/store/dictation/useDictationState.js.map +1 -1
- package/lib/commonjs/ai-components/store/index.js +22 -0
- package/lib/commonjs/ai-components/store/index.js.map +1 -1
- package/lib/commonjs/ai-components/theme.js +3 -0
- package/lib/commonjs/ai-components/theme.js.map +1 -1
- package/lib/commonjs/ai-components/transcription/useDictation.js +7 -3
- package/lib/commonjs/ai-components/transcription/useDictation.js.map +1 -1
- package/lib/commonjs/ai-components/transcription/useDictationAudioLevel.js +32 -0
- package/lib/commonjs/ai-components/transcription/useDictationAudioLevel.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/VoiceGlow.js +79 -0
- package/lib/commonjs/ai-components/voice-visualization/VoiceGlow.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/VoiceVisualizationView.js +147 -0
- package/lib/commonjs/ai-components/voice-visualization/VoiceVisualizationView.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/VoiceWave.js +58 -0
- package/lib/commonjs/ai-components/voice-visualization/VoiceWave.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/bounce.js +60 -0
- package/lib/commonjs/ai-components/voice-visualization/bounce.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/driver.js +208 -0
- package/lib/commonjs/ai-components/voice-visualization/driver.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/glowShader.js +218 -0
- package/lib/commonjs/ai-components/voice-visualization/glowShader.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/index.js +18 -0
- package/lib/commonjs/ai-components/voice-visualization/index.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/math.js +70 -0
- package/lib/commonjs/ai-components/voice-visualization/math.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/palette.js +47 -0
- package/lib/commonjs/ai-components/voice-visualization/palette.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/simplex.js +77 -0
- package/lib/commonjs/ai-components/voice-visualization/simplex.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/styles.js +33 -0
- package/lib/commonjs/ai-components/voice-visualization/styles.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/types.js +4 -0
- package/lib/commonjs/ai-components/voice-visualization/types.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/useReduceMotion.js +28 -0
- package/lib/commonjs/ai-components/voice-visualization/useReduceMotion.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/useVoiceVisualization.js +129 -0
- package/lib/commonjs/ai-components/voice-visualization/useVoiceVisualization.js.map +1 -0
- package/lib/commonjs/ai-components/voice-visualization/waveGeometry.js +124 -0
- package/lib/commonjs/ai-components/voice-visualization/waveGeometry.js.map +1 -0
- package/lib/commonjs/components/Channel/Channel.js +1 -0
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/ChannelShell.js +8 -1
- package/lib/commonjs/components/Channel/ChannelShell.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js +2 -1
- package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useJumpToLatestOnOpen.js +26 -0
- package/lib/commonjs/components/Channel/hooks/useJumpToLatestOnOpen.js.map +1 -0
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberItem.js +1 -1
- package/lib/commonjs/components/ChannelDetails/components/members/ChannelMemberItem.js.map +1 -1
- package/lib/commonjs/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js +1 -1
- package/lib/commonjs/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js.map +1 -1
- package/lib/commonjs/components/ChannelList/ChannelListView.js +0 -2
- package/lib/commonjs/components/ChannelList/ChannelListView.js.map +1 -1
- package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js +2 -2
- package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelDetailsBottomSheet.js +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelDetailsBottomSheet.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelLastMessagePreview.js +1 -0
- package/lib/commonjs/components/ChannelPreview/ChannelLastMessagePreview.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +2 -0
- package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelPreviewTitle.js +1 -0
- package/lib/commonjs/components/ChannelPreview/ChannelPreviewTitle.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js +5 -5
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +4 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
- package/lib/commonjs/components/Chat/hooks/useConnectionLifecycle.js +6 -2
- package/lib/commonjs/components/Chat/hooks/useConnectionLifecycle.js.map +1 -1
- package/lib/commonjs/components/Indicators/LoadingErrorIndicator.js +5 -0
- package/lib/commonjs/components/Indicators/LoadingErrorIndicator.js.map +1 -1
- package/lib/commonjs/components/Message/Message.js +1 -1
- package/lib/commonjs/components/Message/Message.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js +4 -0
- package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageWrapper.js +7 -4
- package/lib/commonjs/components/Message/MessageItemView/MessageWrapper.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useCreateMessageActionHandlers.js +3 -3
- package/lib/commonjs/components/Message/hooks/useCreateMessageActionHandlers.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useProcessReactions.js +2 -2
- package/lib/commonjs/components/Message/hooks/useProcessReactions.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useUserMuteActive.js +1 -1
- package/lib/commonjs/components/Message/hooks/useUserMuteActive.js.map +1 -1
- package/lib/commonjs/components/MessageInput/MessageComposer.js +2 -1
- package/lib/commonjs/components/MessageInput/MessageComposer.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageFlashList.js +2 -2
- package/lib/commonjs/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageList.js +2 -2
- package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
- package/lib/commonjs/components/MessageList/hooks/useOwnUnreadCount.js +1 -1
- package/lib/commonjs/components/MessageList/hooks/useOwnUnreadCount.js.map +1 -1
- package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js +1 -1
- package/lib/commonjs/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
- package/lib/commonjs/components/MessageMenu/hooks/useMessageOwnReactions.js +2 -2
- package/lib/commonjs/components/MessageMenu/hooks/useMessageOwnReactions.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollAnswersList.js +1 -1
- package/lib/commonjs/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/commonjs/components/Poll/components/PollButtons.js +1 -1
- package/lib/commonjs/components/Poll/components/PollButtons.js.map +1 -1
- package/lib/commonjs/components/Poll/hook/useIsPollCreatedByCurrentUser.js +1 -1
- package/lib/commonjs/components/Poll/hook/useIsPollCreatedByCurrentUser.js.map +1 -1
- package/lib/commonjs/components/Thread/Thread.js +1 -1
- package/lib/commonjs/components/Thread/Thread.js.map +1 -1
- package/lib/commonjs/components/ThreadList/ThreadListItem.js +1 -1
- package/lib/commonjs/components/ThreadList/ThreadListItem.js.map +1 -1
- package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js +9 -2
- package/lib/commonjs/components/ThreadList/ThreadListUnreadBanner.js.map +1 -1
- package/lib/commonjs/components/index.js +36 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/contexts/componentsContext/defaultComponents.js +0 -2
- package/lib/commonjs/contexts/componentsContext/defaultComponents.js.map +1 -1
- package/lib/commonjs/contexts/liveLocationManagerContext/LiveLocationManagerContext.js +1 -1
- package/lib/commonjs/contexts/liveLocationManagerContext/LiveLocationManagerContext.js.map +1 -1
- package/lib/commonjs/hooks/actions/useChannelActionItems.js +1 -1
- package/lib/commonjs/hooks/actions/useChannelActionItems.js.map +1 -1
- package/lib/commonjs/hooks/actions/useChannelActions.js +2 -2
- package/lib/commonjs/hooks/actions/useChannelActions.js.map +1 -1
- package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js +1 -1
- package/lib/commonjs/hooks/actions/useChannelMemberActionItems.js.map +1 -1
- package/lib/commonjs/hooks/index.js +11 -0
- package/lib/commonjs/hooks/index.js.map +1 -1
- package/lib/commonjs/hooks/messagePreview/useMessageDeliveryStatus.js +6 -6
- package/lib/commonjs/hooks/messagePreview/useMessageDeliveryStatus.js.map +1 -1
- package/lib/commonjs/hooks/useIsDirectChat.js +1 -1
- package/lib/commonjs/hooks/useIsDirectChat.js.map +1 -1
- package/lib/commonjs/i18n/runtimeDefaults.js +1 -0
- package/lib/commonjs/i18n/runtimeDefaults.js.map +1 -1
- package/lib/commonjs/store/apis/getChannelMessages.js +5 -3
- package/lib/commonjs/store/apis/getChannelMessages.js.map +1 -1
- package/lib/commonjs/store/apis/getChannels.js +6 -3
- package/lib/commonjs/store/apis/getChannels.js.map +1 -1
- package/lib/commonjs/store/apis/getChannelsForFilterSort.js +2 -1
- package/lib/commonjs/store/apis/getChannelsForFilterSort.js.map +1 -1
- package/lib/commonjs/store/apis/queries/selectMessagesForChannels.js +7 -5
- package/lib/commonjs/store/apis/queries/selectMessagesForChannels.js.map +1 -1
- package/lib/commonjs/utils/installNativeMultipartAdapter.js.map +1 -1
- package/lib/commonjs/utils/setupCommandUIMiddlewares.js +6 -3
- package/lib/commonjs/utils/setupCommandUIMiddlewares.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/ai-components/README.md +50 -2
- package/lib/module/ai-components/index.js +24 -0
- package/lib/module/ai-components/index.js.map +1 -1
- package/lib/module/ai-components/message-composer/ComposerView.js +4 -2
- package/lib/module/ai-components/message-composer/ComposerView.js.map +1 -1
- package/lib/module/ai-components/message-composer/SpeechToTextButton.js +4 -1
- package/lib/module/ai-components/message-composer/SpeechToTextButton.js.map +1 -1
- package/lib/module/ai-components/native/AIDictation.js +6 -0
- package/lib/module/ai-components/native/AIDictation.js.map +1 -1
- package/lib/module/ai-components/store/dictation/store.js +4 -2
- package/lib/module/ai-components/store/dictation/store.js.map +1 -1
- package/lib/module/ai-components/store/dictation/useDictationState.js +4 -2
- package/lib/module/ai-components/store/dictation/useDictationState.js.map +1 -1
- package/lib/module/ai-components/store/index.js +22 -0
- package/lib/module/ai-components/store/index.js.map +1 -1
- package/lib/module/ai-components/theme.js +3 -0
- package/lib/module/ai-components/theme.js.map +1 -1
- package/lib/module/ai-components/transcription/useDictation.js +7 -3
- package/lib/module/ai-components/transcription/useDictation.js.map +1 -1
- package/lib/module/ai-components/transcription/useDictationAudioLevel.js +32 -0
- package/lib/module/ai-components/transcription/useDictationAudioLevel.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/VoiceGlow.js +79 -0
- package/lib/module/ai-components/voice-visualization/VoiceGlow.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/VoiceVisualizationView.js +147 -0
- package/lib/module/ai-components/voice-visualization/VoiceVisualizationView.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/VoiceWave.js +58 -0
- package/lib/module/ai-components/voice-visualization/VoiceWave.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/bounce.js +60 -0
- package/lib/module/ai-components/voice-visualization/bounce.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/driver.js +208 -0
- package/lib/module/ai-components/voice-visualization/driver.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/glowShader.js +218 -0
- package/lib/module/ai-components/voice-visualization/glowShader.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/index.js +18 -0
- package/lib/module/ai-components/voice-visualization/index.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/math.js +70 -0
- package/lib/module/ai-components/voice-visualization/math.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/palette.js +47 -0
- package/lib/module/ai-components/voice-visualization/palette.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/simplex.js +77 -0
- package/lib/module/ai-components/voice-visualization/simplex.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/styles.js +33 -0
- package/lib/module/ai-components/voice-visualization/styles.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/types.js +4 -0
- package/lib/module/ai-components/voice-visualization/types.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/useReduceMotion.js +28 -0
- package/lib/module/ai-components/voice-visualization/useReduceMotion.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/useVoiceVisualization.js +129 -0
- package/lib/module/ai-components/voice-visualization/useVoiceVisualization.js.map +1 -0
- package/lib/module/ai-components/voice-visualization/waveGeometry.js +124 -0
- package/lib/module/ai-components/voice-visualization/waveGeometry.js.map +1 -0
- package/lib/module/components/Channel/Channel.js +1 -0
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/ChannelShell.js +8 -1
- package/lib/module/components/Channel/ChannelShell.js.map +1 -1
- package/lib/module/components/Channel/hooks/useChannelLifecycle.js +2 -1
- package/lib/module/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
- package/lib/module/components/Channel/hooks/useJumpToLatestOnOpen.js +26 -0
- package/lib/module/components/Channel/hooks/useJumpToLatestOnOpen.js.map +1 -0
- package/lib/module/components/ChannelDetails/components/members/ChannelMemberItem.js +1 -1
- package/lib/module/components/ChannelDetails/components/members/ChannelMemberItem.js.map +1 -1
- package/lib/module/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js +1 -1
- package/lib/module/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.js.map +1 -1
- package/lib/module/components/ChannelList/ChannelListView.js +0 -2
- package/lib/module/components/ChannelList/ChannelListView.js.map +1 -1
- package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js +2 -2
- package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelDetailsBottomSheet.js +1 -1
- package/lib/module/components/ChannelPreview/ChannelDetailsBottomSheet.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelLastMessagePreview.js +1 -0
- package/lib/module/components/ChannelPreview/ChannelLastMessagePreview.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +2 -0
- package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelPreviewTitle.js +1 -0
- package/lib/module/components/ChannelPreview/ChannelPreviewTitle.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelPreviewView.js +1 -1
- package/lib/module/components/ChannelPreview/ChannelPreviewView.js.map +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js +5 -5
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewData.js.map +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayName.js.map +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.js.map +1 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js +4 -1
- package/lib/module/components/ChannelPreview/hooks/useChannelPreviewPollLabel.js.map +1 -1
- package/lib/module/components/Chat/hooks/useConnectionLifecycle.js +6 -2
- package/lib/module/components/Chat/hooks/useConnectionLifecycle.js.map +1 -1
- package/lib/module/components/Indicators/LoadingErrorIndicator.js +5 -0
- package/lib/module/components/Indicators/LoadingErrorIndicator.js.map +1 -1
- package/lib/module/components/Message/Message.js +1 -1
- package/lib/module/components/Message/Message.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageStatus.js +4 -0
- package/lib/module/components/Message/MessageItemView/MessageStatus.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageWrapper.js +7 -4
- package/lib/module/components/Message/MessageItemView/MessageWrapper.js.map +1 -1
- package/lib/module/components/Message/hooks/useCreateMessageActionHandlers.js +3 -3
- package/lib/module/components/Message/hooks/useCreateMessageActionHandlers.js.map +1 -1
- package/lib/module/components/Message/hooks/useProcessReactions.js +2 -2
- package/lib/module/components/Message/hooks/useProcessReactions.js.map +1 -1
- package/lib/module/components/Message/hooks/useUserMuteActive.js +1 -1
- package/lib/module/components/Message/hooks/useUserMuteActive.js.map +1 -1
- package/lib/module/components/MessageInput/MessageComposer.js +2 -1
- package/lib/module/components/MessageInput/MessageComposer.js.map +1 -1
- package/lib/module/components/MessageList/MessageFlashList.js +2 -2
- package/lib/module/components/MessageList/MessageFlashList.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +2 -2
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/components/MessageList/hooks/useOwnUnreadCount.js +1 -1
- package/lib/module/components/MessageList/hooks/useOwnUnreadCount.js.map +1 -1
- package/lib/module/components/MessageMenu/MessageUserReactionsItem.js +1 -1
- package/lib/module/components/MessageMenu/MessageUserReactionsItem.js.map +1 -1
- package/lib/module/components/MessageMenu/hooks/useMessageOwnReactions.js +2 -2
- package/lib/module/components/MessageMenu/hooks/useMessageOwnReactions.js.map +1 -1
- package/lib/module/components/Poll/components/PollAnswersList.js +1 -1
- package/lib/module/components/Poll/components/PollAnswersList.js.map +1 -1
- package/lib/module/components/Poll/components/PollButtons.js +1 -1
- package/lib/module/components/Poll/components/PollButtons.js.map +1 -1
- package/lib/module/components/Poll/hook/useIsPollCreatedByCurrentUser.js +1 -1
- package/lib/module/components/Poll/hook/useIsPollCreatedByCurrentUser.js.map +1 -1
- package/lib/module/components/Thread/Thread.js +1 -1
- package/lib/module/components/Thread/Thread.js.map +1 -1
- package/lib/module/components/ThreadList/ThreadListItem.js +1 -1
- package/lib/module/components/ThreadList/ThreadListItem.js.map +1 -1
- package/lib/module/components/ThreadList/ThreadListUnreadBanner.js +9 -2
- package/lib/module/components/ThreadList/ThreadListUnreadBanner.js.map +1 -1
- package/lib/module/components/index.js +36 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/contexts/componentsContext/defaultComponents.js +0 -2
- package/lib/module/contexts/componentsContext/defaultComponents.js.map +1 -1
- package/lib/module/contexts/liveLocationManagerContext/LiveLocationManagerContext.js +1 -1
- package/lib/module/contexts/liveLocationManagerContext/LiveLocationManagerContext.js.map +1 -1
- package/lib/module/hooks/actions/useChannelActionItems.js +1 -1
- package/lib/module/hooks/actions/useChannelActionItems.js.map +1 -1
- package/lib/module/hooks/actions/useChannelActions.js +2 -2
- package/lib/module/hooks/actions/useChannelActions.js.map +1 -1
- package/lib/module/hooks/actions/useChannelMemberActionItems.js +1 -1
- package/lib/module/hooks/actions/useChannelMemberActionItems.js.map +1 -1
- package/lib/module/hooks/index.js +11 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/messagePreview/useMessageDeliveryStatus.js +6 -6
- package/lib/module/hooks/messagePreview/useMessageDeliveryStatus.js.map +1 -1
- package/lib/module/hooks/useIsDirectChat.js +1 -1
- package/lib/module/hooks/useIsDirectChat.js.map +1 -1
- package/lib/module/i18n/runtimeDefaults.js +1 -0
- package/lib/module/i18n/runtimeDefaults.js.map +1 -1
- package/lib/module/store/apis/getChannelMessages.js +5 -3
- package/lib/module/store/apis/getChannelMessages.js.map +1 -1
- package/lib/module/store/apis/getChannels.js +6 -3
- package/lib/module/store/apis/getChannels.js.map +1 -1
- package/lib/module/store/apis/getChannelsForFilterSort.js +2 -1
- package/lib/module/store/apis/getChannelsForFilterSort.js.map +1 -1
- package/lib/module/store/apis/queries/selectMessagesForChannels.js +7 -5
- package/lib/module/store/apis/queries/selectMessagesForChannels.js.map +1 -1
- package/lib/module/utils/installNativeMultipartAdapter.js.map +1 -1
- package/lib/module/utils/setupCommandUIMiddlewares.js +6 -3
- package/lib/module/utils/setupCommandUIMiddlewares.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/ai-components/index.d.ts +3 -1
- package/lib/typescript/ai-components/index.d.ts.map +1 -1
- package/lib/typescript/ai-components/message-composer/ComposerView.d.ts.map +1 -1
- package/lib/typescript/ai-components/native/AIDictation.d.ts +6 -0
- package/lib/typescript/ai-components/native/AIDictation.d.ts.map +1 -1
- package/lib/typescript/ai-components/store/dictation/store.d.ts +7 -0
- package/lib/typescript/ai-components/store/dictation/store.d.ts.map +1 -1
- package/lib/typescript/ai-components/store/dictation/useDictationState.d.ts +1 -0
- package/lib/typescript/ai-components/store/dictation/useDictationState.d.ts.map +1 -1
- package/lib/typescript/ai-components/store/index.d.ts +2 -0
- package/lib/typescript/ai-components/store/index.d.ts.map +1 -1
- package/lib/typescript/ai-components/theme.d.ts +13 -0
- package/lib/typescript/ai-components/theme.d.ts.map +1 -1
- package/lib/typescript/ai-components/transcription/useDictation.d.ts +9 -1
- package/lib/typescript/ai-components/transcription/useDictation.d.ts.map +1 -1
- package/lib/typescript/ai-components/transcription/useDictationAudioLevel.d.ts +11 -0
- package/lib/typescript/ai-components/transcription/useDictationAudioLevel.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/VoiceGlow.d.ts +13 -0
- package/lib/typescript/ai-components/voice-visualization/VoiceGlow.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/VoiceVisualizationView.d.ts +26 -0
- package/lib/typescript/ai-components/voice-visualization/VoiceVisualizationView.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/VoiceWave.d.ts +13 -0
- package/lib/typescript/ai-components/voice-visualization/VoiceWave.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/bounce.d.ts +20 -0
- package/lib/typescript/ai-components/voice-visualization/bounce.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/driver.d.ts +96 -0
- package/lib/typescript/ai-components/voice-visualization/driver.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts +7 -0
- package/lib/typescript/ai-components/voice-visualization/glowShader.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/index.d.ts +4 -0
- package/lib/typescript/ai-components/voice-visualization/index.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/math.d.ts +26 -0
- package/lib/typescript/ai-components/voice-visualization/math.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/palette.d.ts +18 -0
- package/lib/typescript/ai-components/voice-visualization/palette.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/simplex.d.ts +11 -0
- package/lib/typescript/ai-components/voice-visualization/simplex.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/styles.d.ts +24 -0
- package/lib/typescript/ai-components/voice-visualization/styles.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/types.d.ts +45 -0
- package/lib/typescript/ai-components/voice-visualization/types.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/useReduceMotion.d.ts +10 -0
- package/lib/typescript/ai-components/voice-visualization/useReduceMotion.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/useVoiceVisualization.d.ts +18 -0
- package/lib/typescript/ai-components/voice-visualization/useVoiceVisualization.d.ts.map +1 -0
- package/lib/typescript/ai-components/voice-visualization/waveGeometry.d.ts +25 -0
- package/lib/typescript/ai-components/voice-visualization/waveGeometry.d.ts.map +1 -0
- package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
- package/lib/typescript/components/Channel/ChannelShell.d.ts +2 -0
- package/lib/typescript/components/Channel/ChannelShell.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useChannelLifecycle.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useJumpToLatestOnOpen.d.ts +15 -0
- package/lib/typescript/components/Channel/hooks/useJumpToLatestOnOpen.d.ts.map +1 -0
- package/lib/typescript/components/ChannelList/ChannelListView.d.ts +1 -1
- package/lib/typescript/components/ChannelList/ChannelListView.d.ts.map +1 -1
- package/lib/typescript/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.d.ts.map +1 -1
- package/lib/typescript/components/ChannelPreview/hooks/useChannelPreviewData.d.ts.map +1 -1
- package/lib/typescript/components/Chat/hooks/useConnectionLifecycle.d.ts +5 -4
- package/lib/typescript/components/Chat/hooks/useConnectionLifecycle.d.ts.map +1 -1
- package/lib/typescript/components/Indicators/LoadingErrorIndicator.d.ts +1 -1
- package/lib/typescript/components/Indicators/LoadingErrorIndicator.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/MessageComposer.d.ts.map +1 -1
- package/lib/typescript/components/ThreadList/ThreadListUnreadBanner.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +3 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts +0 -1
- package/lib/typescript/contexts/componentsContext/defaultComponents.d.ts.map +1 -1
- package/lib/typescript/hooks/index.d.ts +1 -0
- package/lib/typescript/hooks/index.d.ts.map +1 -1
- package/lib/typescript/hooks/messagePreview/useMessageDeliveryStatus.d.ts +3 -0
- package/lib/typescript/hooks/messagePreview/useMessageDeliveryStatus.d.ts.map +1 -1
- package/lib/typescript/i18n/keys.d.ts +3 -1
- package/lib/typescript/i18n/keys.d.ts.map +1 -1
- package/lib/typescript/i18n/runtimeDefaults.d.ts +1 -0
- package/lib/typescript/i18n/runtimeDefaults.d.ts.map +1 -1
- package/lib/typescript/store/apis/getChannelMessages.d.ts +2 -1
- package/lib/typescript/store/apis/getChannelMessages.d.ts.map +1 -1
- package/lib/typescript/store/apis/getChannels.d.ts +3 -1
- package/lib/typescript/store/apis/getChannels.d.ts.map +1 -1
- package/lib/typescript/store/apis/getChannelsForFilterSort.d.ts.map +1 -1
- package/lib/typescript/store/apis/queries/selectMessagesForChannels.d.ts +3 -1
- package/lib/typescript/store/apis/queries/selectMessagesForChannels.d.ts.map +1 -1
- package/lib/typescript/utils/installNativeMultipartAdapter.d.ts +5 -3
- package/lib/typescript/utils/installNativeMultipartAdapter.d.ts.map +1 -1
- package/lib/typescript/utils/setupCommandUIMiddlewares.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/offline-support/offline-feature.tsx +7 -7
- package/src/__tests__/offline-support/optimistic-update.tsx +5 -5
- package/src/ai-components/README.md +50 -2
- package/src/ai-components/__tests__/VoiceVisualization.test.tsx +208 -0
- package/src/ai-components/__tests__/dictation.test.tsx +58 -1
- package/src/ai-components/__tests__/voiceDriver.test.ts +317 -0
- package/src/ai-components/__tests__/voicePalette.test.ts +124 -0
- package/src/ai-components/index.ts +3 -1
- package/src/ai-components/message-composer/ComposerView.tsx +6 -3
- package/src/ai-components/message-composer/SpeechToTextButton.tsx +5 -2
- package/src/ai-components/native/AIDictation.ts +11 -0
- package/src/ai-components/store/dictation/store.ts +9 -0
- package/src/ai-components/store/dictation/useDictationState.ts +2 -1
- package/src/ai-components/store/index.ts +2 -0
- package/src/ai-components/theme.ts +16 -0
- package/src/ai-components/transcription/useDictation.ts +16 -3
- package/src/ai-components/transcription/useDictationAudioLevel.ts +38 -0
- package/src/ai-components/voice-visualization/VoiceGlow.tsx +79 -0
- package/src/ai-components/voice-visualization/VoiceVisualizationView.tsx +179 -0
- package/src/ai-components/voice-visualization/VoiceWave.tsx +67 -0
- package/src/ai-components/voice-visualization/bounce.ts +61 -0
- package/src/ai-components/voice-visualization/driver.ts +240 -0
- package/src/ai-components/voice-visualization/glowShader.ts +227 -0
- package/src/ai-components/voice-visualization/index.ts +3 -0
- package/src/ai-components/voice-visualization/math.ts +73 -0
- package/src/ai-components/voice-visualization/palette.ts +59 -0
- package/src/ai-components/voice-visualization/simplex.ts +92 -0
- package/src/ai-components/voice-visualization/styles.ts +42 -0
- package/src/ai-components/voice-visualization/types.ts +44 -0
- package/src/ai-components/voice-visualization/useReduceMotion.ts +34 -0
- package/src/ai-components/voice-visualization/useVoiceVisualization.ts +153 -0
- package/src/ai-components/voice-visualization/waveGeometry.ts +137 -0
- package/src/components/Channel/Channel.tsx +1 -0
- package/src/components/Channel/ChannelShell.tsx +12 -0
- package/src/components/Channel/__tests__/Channel.test.tsx +146 -5
- package/src/components/Channel/hooks/useChannelLifecycle.ts +4 -1
- package/src/components/Channel/hooks/useJumpToLatestOnOpen.ts +42 -0
- package/src/components/ChannelDetails/__tests__/ChannelDetails.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/ChannelDetailsActionsSection.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/ChannelDetailsEditButton.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/ChannelDetailsMemberSection.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/ChannelDetailsNavHeader.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/ChannelDetailsProfile.test.tsx +2 -2
- package/src/components/ChannelDetails/__tests__/ChannelEditDetailsForm.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/ChannelEditDetailsFormContent.test.tsx +2 -2
- package/src/components/ChannelDetails/__tests__/ChannelEditName.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/members/ChannelAddMembersForm.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/members/ChannelMemberActionsSheet.test.tsx +2 -2
- package/src/components/ChannelDetails/__tests__/members/ChannelMemberItem.test.tsx +1 -1
- package/src/components/ChannelDetails/__tests__/useChannelDetailsMemberStatusText.test.tsx +1 -1
- package/src/components/ChannelDetails/components/members/ChannelMemberItem.tsx +1 -1
- package/src/components/ChannelDetails/hooks/useChannelDetailsMemberStatusText.ts +1 -1
- package/src/components/ChannelList/ChannelListView.tsx +1 -3
- package/src/components/ChannelList/__tests__/ChannelList.test.tsx +72 -0
- package/src/components/ChannelList/hooks/usePaginatedChannels.ts +2 -2
- package/src/components/ChannelPreview/ChannelDetailsBottomSheet.tsx +1 -1
- package/src/components/ChannelPreview/ChannelLastMessagePreview.tsx +1 -1
- package/src/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.tsx +8 -2
- package/src/components/ChannelPreview/ChannelPreviewTitle.tsx +1 -1
- package/src/components/ChannelPreview/ChannelPreviewView.tsx +1 -1
- package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx +4 -4
- package/src/components/ChannelPreview/__tests__/ChannelPreviewView.test.tsx +40 -0
- package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewData.test.tsx +51 -0
- package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayName.test.tsx +1 -1
- package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewDisplayPresence.test.tsx +1 -1
- package/src/components/ChannelPreview/hooks/__tests__/useChannelPreviewPollLabel.test.tsx +87 -0
- package/src/components/ChannelPreview/hooks/useChannelPreviewData.ts +3 -4
- package/src/components/ChannelPreview/hooks/useChannelPreviewDisplayName.ts +1 -1
- package/src/components/ChannelPreview/hooks/useChannelPreviewDisplayPresence.ts +1 -1
- package/src/components/ChannelPreview/hooks/useChannelPreviewPollLabel.ts +1 -1
- package/src/components/Chat/__tests__/Chat.test.tsx +31 -1
- package/src/components/Chat/hooks/useConnectionLifecycle.ts +12 -6
- package/src/components/Indicators/LoadingErrorIndicator.tsx +8 -1
- package/src/components/Message/Message.tsx +1 -1
- package/src/components/Message/MessageItemView/MessageStatus.tsx +16 -2
- package/src/components/Message/MessageItemView/MessageWrapper.tsx +14 -4
- package/src/components/Message/MessageItemView/__tests__/MessageStatus.test.tsx +21 -0
- package/src/components/Message/hooks/__tests__/useCreateMessageActionHandlers.test.tsx +1 -1
- package/src/components/Message/hooks/__tests__/useUserMuteActive.test.tsx +1 -1
- package/src/components/Message/hooks/useCreateMessageActionHandlers.ts +3 -3
- package/src/components/Message/hooks/useProcessReactions.ts +2 -2
- package/src/components/Message/hooks/useUserMuteActive.ts +1 -1
- package/src/components/MessageInput/MessageComposer.tsx +4 -2
- package/src/components/MessageList/MessageFlashList.tsx +2 -2
- package/src/components/MessageList/MessageList.tsx +2 -2
- package/src/components/MessageList/__tests__/MessageList.test.tsx +115 -14
- package/src/components/MessageList/__tests__/ScrollToBottomButton.test.tsx +3 -3
- package/src/components/MessageList/hooks/useOwnUnreadCount.ts +1 -1
- package/src/components/MessageMenu/MessageUserReactionsItem.tsx +1 -1
- package/src/components/MessageMenu/hooks/useMessageOwnReactions.ts +2 -2
- package/src/components/Poll/components/PollAnswersList.tsx +1 -1
- package/src/components/Poll/components/PollButtons.tsx +1 -1
- package/src/components/Poll/hook/useIsPollCreatedByCurrentUser.ts +1 -1
- package/src/components/Thread/Thread.tsx +1 -1
- package/src/components/Thread/__tests__/Thread.test.tsx +89 -2
- package/src/components/ThreadList/ThreadListItem.tsx +1 -1
- package/src/components/ThreadList/ThreadListUnreadBanner.tsx +6 -2
- package/src/components/ThreadList/__tests__/ThreadList.test.tsx +21 -0
- package/src/components/index.ts +3 -0
- package/src/contexts/componentsContext/defaultComponents.ts +0 -2
- package/src/contexts/liveLocationManagerContext/LiveLocationManagerContext.tsx +1 -1
- package/src/hooks/__tests__/useChannelMuteActive.test.tsx +1 -1
- package/src/hooks/__tests__/useIsDirectChat.test.tsx +1 -1
- package/src/hooks/actions/__tests__/useChannelActionItems.test.tsx +1 -1
- package/src/hooks/actions/__tests__/useChannelActions.test.tsx +1 -1
- package/src/hooks/actions/__tests__/useChannelMemberActionItems.test.tsx +6 -6
- package/src/hooks/actions/useChannelActionItems.tsx +1 -1
- package/src/hooks/actions/useChannelActions.ts +2 -2
- package/src/hooks/actions/useChannelMemberActionItems.tsx +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/messagePreview/__tests__/useMessageDeliveryStatus.test.tsx +57 -32
- package/src/hooks/messagePreview/useMessageDeliveryStatus.ts +13 -7
- package/src/hooks/useIsDirectChat.ts +1 -1
- package/src/i18n/__tests__/catalog.fixture.json +2 -0
- package/src/i18n/keys.ts +3 -0
- package/src/i18n/runtimeDefaults.ts +1 -0
- package/src/store/__tests__/selectMessagesForChannels.test.ts +34 -12
- package/src/store/apis/getChannelMessages.ts +4 -1
- package/src/store/apis/getChannels.ts +5 -1
- package/src/store/apis/getChannelsForFilterSort.ts +1 -0
- package/src/store/apis/queries/selectMessagesForChannels.ts +7 -2
- package/src/utils/__tests__/setupCommandUIMiddlewares.test.ts +49 -0
- package/src/utils/installNativeMultipartAdapter.ts +5 -3
- package/src/utils/setupCommandUIMiddlewares.ts +3 -0
- 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
|
|
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(
|
|
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
|
|
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(
|
|
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 ||
|
|
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(),
|
|
31
|
+
value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
|
|
32
32
|
>
|
|
33
33
|
{children}
|
|
34
34
|
</ChatContext.Provider>
|
|
@@ -63,7 +63,7 @@ const Providers = ({ children }: PropsWithChildren) => (
|
|
|
63
63
|
}}
|
|
64
64
|
>
|
|
65
65
|
<ChatContext.Provider
|
|
66
|
-
value={{ client: { notifications: new NotificationManager(),
|
|
66
|
+
value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
|
|
67
67
|
>
|
|
68
68
|
{children}
|
|
69
69
|
</ChatContext.Provider>
|
|
@@ -39,7 +39,7 @@ const Providers = ({ children }: PropsWithChildren) => (
|
|
|
39
39
|
}}
|
|
40
40
|
>
|
|
41
41
|
<ChatContext.Provider
|
|
42
|
-
value={{ client: { notifications: new NotificationManager(),
|
|
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: () => ({
|
|
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: {
|
|
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(),
|
|
76
|
+
value={{ client: { notifications: new NotificationManager(), userId: 'me' } } as never}
|
|
77
77
|
>
|
|
78
78
|
<ChannelDetailsContextProvider channel={channel}>
|
|
79
79
|
<WithComponents overrides={{ ChannelEditDetailsFormContent: EditDetailsProbe }}>
|