diff --git a/docusaurus/docs/reactnative/common-content/ui-components/channel/props/should_show_unread_underlay.mdx b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/should_show_unread_underlay.mdx new file mode 100644 index 0000000000..139b8ea74e --- /dev/null +++ b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/should_show_unread_underlay.mdx @@ -0,0 +1,5 @@ +Boolean to enable/disable the message underlay background when there are unread messages in the Message List. + +| Type | Default | +| ---------------------- | ------- | +| `boolean`\|`undefined` | `true` | diff --git a/docusaurus/docs/reactnative/contexts/messages-context.mdx b/docusaurus/docs/reactnative/contexts/messages-context.mdx index 455c1cc30b..5ace299ae9 100644 --- a/docusaurus/docs/reactnative/contexts/messages-context.mdx +++ b/docusaurus/docs/reactnative/contexts/messages-context.mdx @@ -66,6 +66,7 @@ import OverlayReactionList from '../common-content/ui-components/overlay-provide import ReactionList from '../common-content/ui-components/channel/props/reaction-list.mdx'; import Reply from '../common-content/ui-components/channel/props/reply.mdx'; import ScrollToBottomButton from '../common-content/ui-components/channel/props/scroll-to-bottom-button.mdx'; +import ShouldShowUnreadUnderlay from '../common-content/ui-components/channel/props/should_show_unread_underlay.mdx'; import SelectReaction from '../common-content/ui-components/channel/props/select_reaction.mdx'; import SupportedReactions from '../common-content/ui-components/channel/props/supported_reactions.mdx'; import TypingIndicator from '../common-content/ui-components/channel/props/typing_indicator.mdx'; @@ -235,6 +236,10 @@ Enables quoted-reply state on given message. | ------------------- | | `(message) => void` | +###
_forwarded from [Channel](../../core-components/channel#shouldshowunreadunderlay)_ props
shouldShowUnreadUnderlay {#shouldshowunreadunderlay} + + + ###
_forwarded from [Channel](../../core-components/channel#supportedreactions)_ props
supportedReactions {#supportedreactions} diff --git a/docusaurus/docs/reactnative/core-components/channel-list.mdx b/docusaurus/docs/reactnative/core-components/channel-list.mdx index b8af02ea12..9e99f13625 100644 --- a/docusaurus/docs/reactnative/core-components/channel-list.mdx +++ b/docusaurus/docs/reactnative/core-components/channel-list.mdx @@ -40,6 +40,10 @@ export const App = () => ; ``` +:::note +When receiving channel information from channel events, the filters are not respected; the reason for this is that channel filters can get very complex, and implementing that filtering logic that supports all of the custom filter would be very hard to do. Implementing this on the backend side isn't an option as it is inefficient and has to cater to different filters. So, to help you with it, you will have to override the `notification.message_new` event using the [`onNewMessageNotification`](./channel-list.mdx#onnewmessagenotification) and `message.new` event handlers using the [`onNewMessage`](./channel-list.mdx#onnewmessage) prop of the `ChannelList` component. +::: + ## Context Providers `ChannelList` contains the provider for the `ChannelsContext`. This can be accessed using the corresponding hook. diff --git a/docusaurus/docs/reactnative/core-components/channel.mdx b/docusaurus/docs/reactnative/core-components/channel.mdx index 529e1a459f..cc3c35334a 100644 --- a/docusaurus/docs/reactnative/core-components/channel.mdx +++ b/docusaurus/docs/reactnative/core-components/channel.mdx @@ -134,6 +134,7 @@ import ScrollToBottomButton from '../common-content/ui-components/channel/props/ import SelectReaction from '../common-content/ui-components/channel/props/select_reaction.mdx'; import SendButton from '../common-content/ui-components/channel/props/send_button.mdx'; import SendMessageDisallowedIndicator from '../common-content/ui-components/channel/props/send_message_disallowed_indicator.mdx'; +import ShouldShowUnreadUnderlay from '../common-content/ui-components/channel/props/should_show_unread_underlay.mdx'; import ShowThreadMessageInChannelButton from '../common-content/ui-components/channel/props/show_thread_message_in_channel_button.mdx'; import StartAudioRecordingButton from '../common-content/ui-components/channel/props/start_audio_recording_button.mdx'; import StateUpdateThrottleInterval from '../common-content/ui-components/channel/props/state_update_throttle_interval.mdx'; @@ -702,6 +703,10 @@ Callback function to set the [ref](https://reactjs.org/docs/refs-and-the-dom.htm | --------- | -------------------- | | ref | ref of the TextInput | +### `shouldShowUnreadUnderlay` + + + ### stateUpdateThrottleInterval diff --git a/package/src/components/Channel/Channel.tsx b/package/src/components/Channel/Channel.tsx index 3e27a87d91..7acc9e3058 100644 --- a/package/src/components/Channel/Channel.tsx +++ b/package/src/components/Channel/Channel.tsx @@ -322,6 +322,7 @@ export type ChannelPropsWithContext< | 'OverlayReactionList' | 'ReactionList' | 'Reply' + | 'shouldShowUnreadUnderlay' | 'ScrollToBottomButton' | 'selectReaction' | 'supportedReactions' @@ -582,6 +583,7 @@ const ChannelWithContext = < setTyping, setWatcherCount, setWatchers, + shouldShowUnreadUnderlay = true, shouldSyncChannel, ShowThreadMessageInChannelButton = ShowThreadMessageInChannelButtonDefault, StartAudioRecordingButton = AudioRecordingButtonDefault, @@ -2412,6 +2414,7 @@ const ChannelWithContext = < sendReaction, setEditingState, setQuotedMessageState, + shouldShowUnreadUnderlay, supportedReactions, targetedMessage, TypingIndicator, diff --git a/package/src/components/Channel/hooks/useCreateMessagesContext.ts b/package/src/components/Channel/hooks/useCreateMessagesContext.ts index a0789400be..a2d3bababd 100644 --- a/package/src/components/Channel/hooks/useCreateMessagesContext.ts +++ b/package/src/components/Channel/hooks/useCreateMessagesContext.ts @@ -85,6 +85,7 @@ export const useCreateMessagesContext = < sendReaction, setEditingState, setQuotedMessageState, + shouldShowUnreadUnderlay, supportedReactions, targetedMessage, TypingIndicator, @@ -183,6 +184,7 @@ export const useCreateMessagesContext = < sendReaction, setEditingState, setQuotedMessageState, + shouldShowUnreadUnderlay, supportedReactions, targetedMessage, TypingIndicator, diff --git a/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx b/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx index 2bd8c37299..7ac0173daf 100644 --- a/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx +++ b/package/src/components/ImageGallery/components/ImageGalleryFooter.tsx @@ -28,7 +28,7 @@ const styles = StyleSheet.create({ }, innerContainer: { flexDirection: 'row', - height: 56, + minHeight: 56, }, leftContainer: { flex: 1, diff --git a/package/src/components/ImageGallery/components/ImageGalleryHeader.tsx b/package/src/components/ImageGallery/components/ImageGalleryHeader.tsx index f83b1eacb8..9f4c5ca35e 100644 --- a/package/src/components/ImageGallery/components/ImageGalleryHeader.tsx +++ b/package/src/components/ImageGallery/components/ImageGalleryHeader.tsx @@ -29,7 +29,7 @@ const styles = StyleSheet.create({ }, innerContainer: { flexDirection: 'row', - height: 56, + minHeight: 56, }, leftContainer: { flex: 1, diff --git a/package/src/components/Message/Message.tsx b/package/src/components/Message/Message.tsx index 0196638e29..4ef1a028fd 100644 --- a/package/src/components/Message/Message.tsx +++ b/package/src/components/Message/Message.tsx @@ -305,8 +305,8 @@ const MessageWithContext = < const { client } = chatContext; const { theme: { - colors: { bg_gradient_start, targetedMessageBackground }, - messageSimple: { targetedMessageContainer, targetedMessageUnderlay }, + colors: { targetedMessageBackground }, + messageSimple: { targetedMessageContainer, targetedMessageUnderlay, unreadUnderlayColor }, }, } = useTheme(); @@ -762,7 +762,7 @@ const MessageWithContext = < style={[ style, { - backgroundColor: showUnreadUnderlay ? bg_gradient_start : undefined, + backgroundColor: showUnreadUnderlay ? unreadUnderlayColor : undefined, }, ]} > diff --git a/package/src/components/MessageList/MessageList.tsx b/package/src/components/MessageList/MessageList.tsx index 21dcd5ac72..856f46f129 100644 --- a/package/src/components/MessageList/MessageList.tsx +++ b/package/src/components/MessageList/MessageList.tsx @@ -144,6 +144,7 @@ type MessageListPropsWithContext< | 'ScrollToBottomButton' | 'MessageSystem' | 'myMessageTheme' + | 'shouldShowUnreadUnderlay' | 'TypingIndicator' | 'TypingIndicatorContainer' > & @@ -270,6 +271,7 @@ const MessageListWithContext = < setMessages, setSelectedPicker, setTargetedMessage, + shouldShowUnreadUnderlay, StickyHeader, targetedMessage, thread, @@ -630,7 +632,10 @@ const MessageListWithContext = < const isCurrentMessageUnread = isMessageUnread(index); const showUnreadUnderlay = - !channel.muteStatus().muted && isCurrentMessageUnread && scrollToBottomButtonVisible; + !!shouldShowUnreadUnderlay && + !channel.muteStatus().muted && + isCurrentMessageUnread && + scrollToBottomButtonVisible; const insertInlineUnreadIndicator = showUnreadUnderlay && !isMessageUnread(index + 1); // show only if previous message is read if (message.type === 'system') { @@ -1260,6 +1265,7 @@ export const MessageList = < MessageSystem, myMessageTheme, ScrollToBottomButton, + shouldShowUnreadUnderlay, TypingIndicator, TypingIndicatorContainer, } = useMessagesContext(); @@ -1309,6 +1315,7 @@ export const MessageList = < setMessages, setSelectedPicker, setTargetedMessage, + shouldShowUnreadUnderlay, StickyHeader, targetedMessage, thread, diff --git a/package/src/components/MessageList/utils/getDateSeparators.ts b/package/src/components/MessageList/utils/getDateSeparators.ts index f4aed40d8c..3dd8bc7cc8 100644 --- a/package/src/components/MessageList/utils/getDateSeparators.ts +++ b/package/src/components/MessageList/utils/getDateSeparators.ts @@ -36,8 +36,13 @@ export const getDateSeparators = < const isDeletedMessageVisibleToSender = deletedMessagesVisibilityType === 'sender' || deletedMessagesVisibilityType === 'always'; + const isDeletedMessageVisibleToReceiver = + deletedMessagesVisibilityType === 'receiver' || deletedMessagesVisibilityType === 'always'; + return ( - !isMessageTypeDeleted || (userId === message.user?.id && isDeletedMessageVisibleToSender) + !isMessageTypeDeleted || + (userId === message.user?.id && isDeletedMessageVisibleToSender) || + (userId !== message.user?.id && isDeletedMessageVisibleToReceiver) ); }); diff --git a/package/src/components/MessageList/utils/getGroupStyles.ts b/package/src/components/MessageList/utils/getGroupStyles.ts index 596e950cf1..2a78730128 100644 --- a/package/src/components/MessageList/utils/getGroupStyles.ts +++ b/package/src/components/MessageList/utils/getGroupStyles.ts @@ -4,7 +4,7 @@ import type { PaginatedMessageListContextValue } from '../../../contexts/paginat import type { ThreadContextValue } from '../../../contexts/threadContext/ThreadContext'; import type { DefaultStreamChatGenerics } from '../../../types/types'; import { isEditedMessage } from '../../../utils/utils'; -import type { GroupType } from '../hooks/useMessageList'; +import type { GroupType, MessageType } from '../hooks/useMessageList'; export type GetGroupStylesParams< StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, @@ -19,6 +19,88 @@ export type GetGroupStylesParams< userId?: string; }; +export type GroupStyle = '' | 'middle' | 'top' | 'bottom' | 'single'; + +const getGroupStyle = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, +>( + dateSeparators: DateSeparators, + message: MessageType, + previousMessage: MessageType, + nextMessage: MessageType, + hideDateSeparators?: boolean, + maxTimeBetweenGroupedMessages?: number, +): GroupStyle[] => { + const groupStyles: GroupStyle[] = []; + + const isPrevMessageTypeDeleted = previousMessage?.type === 'deleted'; + const isNextMessageTypeDeleted = nextMessage?.type === 'deleted'; + + const userId = message?.user?.id || null; + + const isTopMessage = + !previousMessage || + previousMessage.type === 'system' || + previousMessage.type === 'error' || + userId !== previousMessage?.user?.id || + !!isPrevMessageTypeDeleted || + (!hideDateSeparators && dateSeparators[message.id]) || + isEditedMessage(previousMessage); + + const isBottomMessage = + !nextMessage || + nextMessage.type === 'system' || + nextMessage.type === 'error' || + userId !== nextMessage?.user?.id || + !!isNextMessageTypeDeleted || + (!hideDateSeparators && dateSeparators[nextMessage.id]) || + (maxTimeBetweenGroupedMessages !== undefined && + (nextMessage.created_at as Date).getTime() - (message.created_at as Date).getTime() > + maxTimeBetweenGroupedMessages) || + isEditedMessage(message); + + /** + * Add group styles key for top message + */ + if (isTopMessage) { + groupStyles.push('top'); + } + + /** + * Add group styles key for bottom message + */ + + const isMessageTypeDeleted = message.type === 'deleted'; + if (isBottomMessage) { + /** + * If the bottom message is also the top, or deleted, or an error, + * add the key for single message instead of bottom + */ + if (isTopMessage || isMessageTypeDeleted || message.type === 'error') { + groupStyles.splice(0, groupStyles.length); + groupStyles.push('single'); + } else { + groupStyles.push('bottom'); + } + } + + /** + * Add the key for all non top or bottom messages, if the message is + * deleted or an error add the key for single otherwise middle + */ + if (!isTopMessage && !isBottomMessage) { + if (isMessageTypeDeleted || message.type === 'error') { + groupStyles.splice(0, groupStyles.length); + groupStyles.push('single'); + } else { + groupStyles.splice(0, groupStyles.length); + groupStyles.push('middle'); + } + } + + return groupStyles; +}; + export const getGroupStyles = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, >( @@ -43,83 +125,19 @@ export const getGroupStyles = < }); for (let i = 0; i < messagesFilteredForNonUser.length; i++) { - const previousMessage = messagesFilteredForNonUser[i - 1] as - | (typeof messagesFilteredForNonUser)[0] - | undefined; + const previousMessage = messagesFilteredForNonUser[i - 1]; const message = messagesFilteredForNonUser[i]; - const nextMessage = messagesFilteredForNonUser[i + 1] as - | (typeof messagesFilteredForNonUser)[0] - | undefined; - const groupStyles: GroupType[] = []; - - const isPrevMessageTypeDeleted = previousMessage?.type === 'deleted'; - const isNextMessageTypeDeleted = nextMessage?.type === 'deleted'; - - const userId = message?.user?.id || null; - - const isTopMessage = - !previousMessage || - previousMessage.type === 'system' || - previousMessage.type === 'error' || - userId !== previousMessage?.user?.id || - !!isPrevMessageTypeDeleted || - (!hideDateSeparators && dateSeparators[message.id]) || - messageGroupStyles[previousMessage.id]?.includes('bottom') || - isEditedMessage(previousMessage); - - const isBottomMessage = - !nextMessage || - nextMessage.type === 'system' || - nextMessage.type === 'error' || - userId !== nextMessage?.user?.id || - !!isNextMessageTypeDeleted || - (!hideDateSeparators && dateSeparators[nextMessage.id]) || - (maxTimeBetweenGroupedMessages !== undefined && - nextMessage.created_at.getTime() - message.created_at.getTime() > - maxTimeBetweenGroupedMessages) || - isEditedMessage(message); - - /** - * Add group styles key for top message - */ - if (isTopMessage) { - groupStyles.push('top'); - } - - /** - * Add group styles key for bottom message - */ - - const isMessageTypeDeleted = message.type === 'deleted'; - if (isBottomMessage) { - /** - * If the bottom message is also the top, or deleted, or an error, - * add the key for single message instead of bottom - */ - if (isTopMessage || isMessageTypeDeleted || message.type === 'error') { - groupStyles.splice(0, groupStyles.length); - groupStyles.push('single'); - } else { - groupStyles.push('bottom'); - } - } - - /** - * Add the key for all non top or bottom messages, if the message is - * deleted or an error add the key for single otherwise middle - */ - if (!isTopMessage && !isBottomMessage) { - if (isMessageTypeDeleted || message.type === 'error') { - groupStyles.splice(0, groupStyles.length); - groupStyles.push('single'); - } else { - groupStyles.splice(0, groupStyles.length); - groupStyles.push('middle'); - } - } + const nextMessage = messagesFilteredForNonUser[i + 1]; if (message.id) { - messageGroupStyles[message.id] = groupStyles; + messageGroupStyles[message.id] = getGroupStyle( + dateSeparators, + message, + previousMessage, + nextMessage, + hideDateSeparators, + maxTimeBetweenGroupedMessages, + ); } } diff --git a/package/src/components/MessageOverlay/OverlayReactionList.tsx b/package/src/components/MessageOverlay/OverlayReactionList.tsx index f3f1e24837..9aee5c70cc 100644 --- a/package/src/components/MessageOverlay/OverlayReactionList.tsx +++ b/package/src/components/MessageOverlay/OverlayReactionList.tsx @@ -120,7 +120,7 @@ export const ReactionButton = < theme: { colors: { accent_blue, grey }, overlay: { - reactionsList: { reaction, reactionSize }, + reactionsList: { reaction, reactionSize, selectedIcon, unSelectedIcon }, }, }, } = useTheme(); @@ -217,9 +217,14 @@ export const ReactionButton = < - + - + diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx index e6970dd079..1069c4031d 100644 --- a/package/src/contexts/messageInputContext/MessageInputContext.tsx +++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx @@ -914,6 +914,7 @@ export const MessageInputProvider = < const prevText = giphyEnabled && giphyActive ? `/giphy ${text}` : text; setText(''); + if (inputBoxRef.current) { inputBoxRef.current.clear(); } diff --git a/package/src/contexts/messagesContext/MessagesContext.tsx b/package/src/contexts/messagesContext/MessagesContext.tsx index bff51ee17b..fe5b441da7 100644 --- a/package/src/contexts/messagesContext/MessagesContext.tsx +++ b/package/src/contexts/messagesContext/MessagesContext.tsx @@ -512,6 +512,11 @@ export type MessagesContextValue< message: MessageType, ) => (reactionType: string) => Promise; + /** + * Boolean to enable/disable the message underlay background when there are unread messages in the Message List. + */ + shouldShowUnreadUnderlay?: boolean; + targetedMessage?: string; }; diff --git a/package/src/contexts/themeContext/utils/theme.ts b/package/src/contexts/themeContext/utils/theme.ts index c0bf575a77..4d3ab825df 100644 --- a/package/src/contexts/themeContext/utils/theme.ts +++ b/package/src/contexts/themeContext/utils/theme.ts @@ -578,6 +578,7 @@ export type Theme = { container: ViewStyle; roundedView: ViewStyle; }; + unreadUnderlayColor?: ColorValue; }; overlay: { container: ViewStyle; @@ -608,6 +609,8 @@ export type Theme = { reactionList: ViewStyle; reactionListBorderRadius: number; reactionSize: number; + selectedIcon: IconProps; + unSelectedIcon: IconProps; }; }; progressControl: { @@ -1200,6 +1203,7 @@ export const defaultTheme: Theme = { }, targetedMessageContainer: {}, targetedMessageUnderlay: {}, + unreadUnderlayColor: Colors.bg_gradient_start, videoThumbnail: { container: {}, roundedView: {}, @@ -1234,6 +1238,8 @@ export const defaultTheme: Theme = { reactionList: {}, reactionListBorderRadius: 24, reactionSize: 24, + selectedIcon: {}, + unSelectedIcon: {}, }, }, progressControl: {