Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next Release #2725

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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` |
5 changes: 5 additions & 0 deletions docusaurus/docs/reactnative/contexts/messages-context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -235,6 +236,10 @@ Enables quoted-reply state on given message.
| ------------------- |
| `(message) => void` |

### <div class="label description">_forwarded from [Channel](../../core-components/channel#shouldshowunreadunderlay)_ props</div> shouldShowUnreadUnderlay {#shouldshowunreadunderlay}

<ShouldShowUnreadUnderlay />

### <div class="label description">_forwarded from [Channel](../../core-components/channel#supportedreactions)_ props</div> supportedReactions {#supportedreactions}

<SupportedReactions />
Expand Down
4 changes: 4 additions & 0 deletions docusaurus/docs/reactnative/core-components/channel-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export const App = () => <OverlayProvider>
</OverlayProvider>;
```

:::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.
Expand Down
5 changes: 5 additions & 0 deletions docusaurus/docs/reactnative/core-components/channel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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`

<ShouldShowUnreadUnderlay />

### stateUpdateThrottleInterval

<StateUpdateThrottleInterval />
Expand Down
3 changes: 3 additions & 0 deletions package/src/components/Channel/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ export type ChannelPropsWithContext<
| 'OverlayReactionList'
| 'ReactionList'
| 'Reply'
| 'shouldShowUnreadUnderlay'
| 'ScrollToBottomButton'
| 'selectReaction'
| 'supportedReactions'
Expand Down Expand Up @@ -582,6 +583,7 @@ const ChannelWithContext = <
setTyping,
setWatcherCount,
setWatchers,
shouldShowUnreadUnderlay = true,
shouldSyncChannel,
ShowThreadMessageInChannelButton = ShowThreadMessageInChannelButtonDefault,
StartAudioRecordingButton = AudioRecordingButtonDefault,
Expand Down Expand Up @@ -2412,6 +2414,7 @@ const ChannelWithContext = <
sendReaction,
setEditingState,
setQuotedMessageState,
shouldShowUnreadUnderlay,
supportedReactions,
targetedMessage,
TypingIndicator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const useCreateMessagesContext = <
sendReaction,
setEditingState,
setQuotedMessageState,
shouldShowUnreadUnderlay,
supportedReactions,
targetedMessage,
TypingIndicator,
Expand Down Expand Up @@ -183,6 +184,7 @@ export const useCreateMessagesContext = <
sendReaction,
setEditingState,
setQuotedMessageState,
shouldShowUnreadUnderlay,
supportedReactions,
targetedMessage,
TypingIndicator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const styles = StyleSheet.create({
},
innerContainer: {
flexDirection: 'row',
height: 56,
minHeight: 56,
},
leftContainer: {
flex: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const styles = StyleSheet.create({
},
innerContainer: {
flexDirection: 'row',
height: 56,
minHeight: 56,
},
leftContainer: {
flex: 1,
Expand Down
6 changes: 3 additions & 3 deletions package/src/components/Message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -762,7 +762,7 @@ const MessageWithContext = <
style={[
style,
{
backgroundColor: showUnreadUnderlay ? bg_gradient_start : undefined,
backgroundColor: showUnreadUnderlay ? unreadUnderlayColor : undefined,
},
]}
>
Expand Down
9 changes: 8 additions & 1 deletion package/src/components/MessageList/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ type MessageListPropsWithContext<
| 'ScrollToBottomButton'
| 'MessageSystem'
| 'myMessageTheme'
| 'shouldShowUnreadUnderlay'
| 'TypingIndicator'
| 'TypingIndicatorContainer'
> &
Expand Down Expand Up @@ -270,6 +271,7 @@ const MessageListWithContext = <
setMessages,
setSelectedPicker,
setTargetedMessage,
shouldShowUnreadUnderlay,
StickyHeader,
targetedMessage,
thread,
Expand Down Expand Up @@ -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') {
Expand Down Expand Up @@ -1260,6 +1265,7 @@ export const MessageList = <
MessageSystem,
myMessageTheme,
ScrollToBottomButton,
shouldShowUnreadUnderlay,
TypingIndicator,
TypingIndicatorContainer,
} = useMessagesContext<StreamChatGenerics>();
Expand Down Expand Up @@ -1309,6 +1315,7 @@ export const MessageList = <
setMessages,
setSelectedPicker,
setTargetedMessage,
shouldShowUnreadUnderlay,
StickyHeader,
targetedMessage,
thread,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
});

Expand Down
168 changes: 93 additions & 75 deletions package/src/components/MessageList/utils/getGroupStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<StreamChatGenerics>,
previousMessage: MessageType<StreamChatGenerics>,
nextMessage: MessageType<StreamChatGenerics>,
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,
>(
Expand All @@ -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,
);
}
}

Expand Down
Loading
Loading