Skip to content
Closed
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
3 changes: 3 additions & 0 deletions package/src/components/Channel/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ export type ChannelPropsWithContext = Pick<ChannelContextValue, 'channel'> &
| 'urlPreviewType'
| 'FlatList'
| 'forceAlignMessages'
| 'getDateSeparators'
| 'getMessageGroupStyle'
| 'giphyVersion'
| 'handleBan'
Expand Down Expand Up @@ -426,6 +427,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
FlatList = NativeHandlers.FlatList,
focusInputOnPickerClose = true,
forceAlignMessages,
getDateSeparators,
getMessageGroupStyle,
handleAttachButtonPress,
handleBan,
Expand Down Expand Up @@ -1693,6 +1695,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
enableSwipeToReply,
FlatList,
forceAlignMessages,
getDateSeparators,
getMessageGroupStyle,
giphyVersion,
handleBan,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const useCreateMessagesContext = ({
enableSwipeToReply,
FlatList,
forceAlignMessages,
getDateSeparators,
getMessageGroupStyle,
giphyVersion,
handleBan,
Expand Down Expand Up @@ -78,6 +79,7 @@ export const useCreateMessagesContext = ({
enableSwipeToReply,
FlatList,
forceAlignMessages,
getDateSeparators,
getMessageGroupStyle,
giphyVersion,
handleBan,
Expand Down
40 changes: 36 additions & 4 deletions package/src/components/Message/MessageItemView/MessageWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,20 @@ export type MessageWrapperProps = {
message: LocalMessage;
previousMessage?: LocalMessage;
nextMessage?: LocalMessage;
/** Set only when a `getDateSeparators` override resolved the separators at the list level. */
dateSeparatorDate?: Date;
/** The separator above the next message, needed to close a message group. Same condition. */
nextMessageDateSeparatorDate?: Date;
};

export const MessageWrapper = React.memo(function MessageWrapper(props: MessageWrapperProps) {
const { message, previousMessage, nextMessage } = props;
const {
message,
previousMessage,
nextMessage,
dateSeparatorDate: resolvedDateSeparatorDate,
nextMessageDateSeparatorDate: resolvedNextMessageDateSeparatorDate,
} = props;
const { client } = useChatContext();
const {
channelUnreadStateStore,
Expand All @@ -43,14 +53,32 @@ export const MessageWrapper = React.memo(function MessageWrapper(props: MessageW
} = useChannelContext();
const { InlineDateSeparator, InlineUnreadIndicator, Message, MessageSystem } =
useComponentsContext();
const { getMessageGroupStyle, myMessageTheme, shouldShowUnreadUnderlay } = useMessagesContext();
const { getDateSeparators, getMessageGroupStyle, myMessageTheme, shouldShowUnreadUnderlay } =
useMessagesContext();
const { goToMessage, onThreadSelect, noGroupByUser, modifiedTheme } = useMessageListItemContext();

const dateSeparatorDate = useMessageDateSeparator({
// With an override the list resolved every separator already; without one the rule is
// neighbour-local, so the row derives its own and the list walks nothing.
const separatorsResolved = !!getDateSeparators;

// The default rule is neighbour-local, so the row derives it without the list walking anything.
const localDateSeparatorDate = useMessageDateSeparator({
hideDateSeparators,
message,
previousMessage,
skip: separatorsResolved,
});
// Needed to close a message group when the next row starts a new day.
const localNextMessageDateSeparatorDate = useMessageDateSeparator({
message: nextMessage,
previousMessage: message,
skip: separatorsResolved,
});

const dateSeparatorDate = separatorsResolved ? resolvedDateSeparatorDate : localDateSeparatorDate;
const nextMessageDateSeparatorDate = separatorsResolved
? resolvedNextMessageDateSeparatorDate
: localNextMessageDateSeparatorDate;

const isNewestMessage = nextMessage === undefined;
const groupStyles = useMessageGroupStyles({
Expand All @@ -60,6 +88,7 @@ export const MessageWrapper = React.memo(function MessageWrapper(props: MessageW
message,
previousMessage,
nextMessage,
nextMessageDateSeparatorDate,
noGroupByUser,
});

Expand Down Expand Up @@ -111,7 +140,10 @@ export const MessageWrapper = React.memo(function MessageWrapper(props: MessageW
return (
<View testID={`message-list-item-${message.id}`}>
{message.type === 'system' ? (
<MessageSystem message={message} style={messageContainer} />
<>
{renderDateSeperator}
<MessageSystem message={message} style={messageContainer} />
</>
) : wrapMessageInTheme ? (
<ThemeProvider mergedStyle={modifiedTheme}>
{renderDateSeperator}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('parseLinksFromText', () => {
'https://localhost/with/path?and=query#fragment',
],
['reactnative.dev', 'http://reactnative.dev'],
['hinge.health/schedule-with-a-coach', 'http://hinge.health/schedule-with-a-coach'],
['example.com/some-page', 'http://example.com/some-page'],
['https://zh.wikipedia.org/wiki/挪威牛油危機', 'https://zh.wikipedia.org/wiki/挪威牛油危機'],
[
'https://getstream.io/chat/docs/react-native/?language=javascript',
Expand Down
8 changes: 7 additions & 1 deletion package/src/components/MessageList/MessageFlashList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Animated from 'react-native-reanimated';
import type { FlashListProps, FlashListRef } from '@shopify/flash-list';
import type { Channel, Event, LocalMessage, MessageResponse } from 'stream-chat';

import { useDateSeparatorDates } from './hooks/useDateSeparatorDates';
import { useMessageList } from './hooks/useMessageList';
import { useScrollToBottomAccessibilityAction } from './hooks/useScrollToBottomAccessibilityAction';
import { useShouldScrollToRecentOnNewOwnMessage } from './hooks/useShouldScrollToRecentOnNewOwnMessage';
Expand Down Expand Up @@ -409,6 +410,9 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
threadList,
});

// This list is ordered oldest -> newest, unlike the inverted `MessageList`.
const dateSeparatorDates = useDateSeparatorDates(processedMessageList, false);

const renderItem = useCallback(
({ item: message, index }: { item: LocalMessage; index: number }) => {
const previousMessage = processedMessageList[index - 1];
Expand All @@ -418,10 +422,12 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
message={message}
previousMessage={previousMessage}
nextMessage={nextMessage}
dateSeparatorDate={dateSeparatorDates?.[index]}
nextMessageDateSeparatorDate={dateSeparatorDates?.[index + 1]}
/>
);
},
[processedMessageList],
[processedMessageList, dateSeparatorDates],
);

/**
Expand Down
20 changes: 18 additions & 2 deletions package/src/components/MessageList/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import debounce from 'lodash/debounce';

import type { Channel, Event, LocalMessage, MessageResponse } from 'stream-chat';

import { useDateSeparatorDates } from './hooks/useDateSeparatorDates';
import { useMessageList } from './hooks/useMessageList';
import { useScrollToBottomAccessibilityAction } from './hooks/useScrollToBottomAccessibilityAction';
import { useShouldScrollToRecentOnNewOwnMessage } from './hooks/useShouldScrollToRecentOnNewOwnMessage';
Expand Down Expand Up @@ -413,6 +414,8 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {

const previousDerivedItemsRef = useRef<Map<string, MessageListItemWithNeighbours>>(undefined);

const dateSeparatorDates = useDateSeparatorDates(processedMessageList, true);

const processedMessageListWithNeighbors = useMemo(() => {
if (!previousDerivedItemsRef.current) {
previousDerivedItemsRef.current = new Map();
Expand All @@ -421,19 +424,28 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
const { items, nextDerivedItems } = buildMessageListWithNeighbours(
processedMessageList,
previousDerivedItemsRef.current,
dateSeparatorDates,
);
previousDerivedItemsRef.current = nextDerivedItems;

return items;
}, [processedMessageList]);
}, [processedMessageList, dateSeparatorDates]);

const renderItem = useStableCallback(({ item }: { item: MessageListItemWithNeighbours }) => {
const { message, previousMessage, nextMessage } = item;
const {
message,
previousMessage,
nextMessage,
dateSeparatorDate,
nextMessageDateSeparatorDate,
} = item;
return (
<MessageWrapper
message={message}
previousMessage={previousMessage}
nextMessage={nextMessage}
dateSeparatorDate={dateSeparatorDate}
nextMessageDateSeparatorDate={nextMessageDateSeparatorDate}
/>
);
});
Expand Down Expand Up @@ -531,10 +543,14 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
return;
}
const isMessageTypeDeleted = lastMessage.type === 'deleted';
// System messages do not anchor date separators in the list, so they must not drive the
// sticky header either - otherwise the header announces a day the list never separates.
const isMessageTypeSystem = lastMessage.type === 'system';

if (
lastMessage?.created_at &&
!isMessageTypeDeleted &&
!isMessageTypeSystem &&
typeof lastMessage.created_at !== 'string' &&
lastMessage.created_at.toDateString() !== stickyHeaderDateRef.current?.toDateString()
) {
Expand Down
Loading
Loading