diff --git a/examples/SampleApp/ios/Podfile.lock b/examples/SampleApp/ios/Podfile.lock
index acfbd0474e..5af66105b1 100644
--- a/examples/SampleApp/ios/Podfile.lock
+++ b/examples/SampleApp/ios/Podfile.lock
@@ -2036,65 +2036,6 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- - react-native-keyboard-controller (1.20.2):
- - boost
- - DoubleConversion
- - fast_float
- - fmt
- - glog
- - hermes-engine
- - RCT-Folly
- - RCT-Folly/Fabric
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - react-native-keyboard-controller/common (= 1.20.2)
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - SocketRocket
- - Yoga
- - react-native-keyboard-controller/common (1.20.2):
- - boost
- - DoubleConversion
- - fast_float
- - fmt
- - glog
- - hermes-engine
- - RCT-Folly
- - RCT-Folly/Fabric
- - RCTRequired
- - RCTTypeSafety
- - React-Core
- - React-debug
- - React-Fabric
- - React-featureflags
- - React-graphics
- - React-hermes
- - React-ImageManager
- - React-jsi
- - React-NativeModulesApple
- - React-RCTFabric
- - React-renderercss
- - React-rendererdebug
- - React-utils
- - ReactCodegen
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- - SocketRocket
- - Yoga
- react-native-maps (1.20.1):
- React-Core
- react-native-netinfo (11.4.1):
@@ -3338,7 +3279,6 @@ DEPENDENCIES:
- "react-native-document-picker (from `../node_modules/@react-native-documents/picker`)"
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
- - react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`)
- react-native-maps (from `../node_modules/react-native-maps`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
@@ -3514,8 +3454,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/geolocation"
react-native-image-picker:
:path: "../node_modules/react-native-image-picker"
- react-native-keyboard-controller:
- :path: "../node_modules/react-native-keyboard-controller"
react-native-maps:
:path: "../node_modules/react-native-maps"
react-native-netinfo:
@@ -3685,7 +3623,6 @@ SPEC CHECKSUMS:
react-native-document-picker: c5fa18e9fc47b34cfbab3b0a4447d0df918a5621
react-native-geolocation: eb39c815c9b58ddc3efb552cafdd4b035e4cf682
react-native-image-picker: e479ec8884df9d99a62c1f53f2307055ad43ea85
- react-native-keyboard-controller: 6fe65d5d011d88e651d5279396e95e9c1f9458ca
react-native-maps: ee1e65647460c3d41e778071be5eda10e3da6225
react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac
react-native-safe-area-context: 7fd4c2c8023da8e18eaa3424cb49d52f626debee
diff --git a/examples/SampleApp/ios/SampleApp-tvOS/Info.plist b/examples/SampleApp/ios/SampleApp-tvOS/Info.plist
index a8d20ea4c7..5bbd1e0da7 100644
--- a/examples/SampleApp/ios/SampleApp-tvOS/Info.plist
+++ b/examples/SampleApp/ios/SampleApp-tvOS/Info.plist
@@ -50,4 +50,4 @@
UIViewControllerBasedStatusBarAppearance
-
\ No newline at end of file
+
diff --git a/examples/SampleApp/ios/SampleApp/Info.plist b/examples/SampleApp/ios/SampleApp/Info.plist
index ae8b0f4d7d..b072573cc4 100644
--- a/examples/SampleApp/ios/SampleApp/Info.plist
+++ b/examples/SampleApp/ios/SampleApp/Info.plist
@@ -59,4 +59,4 @@
UIViewControllerBasedStatusBarAppearance
-
\ No newline at end of file
+
diff --git a/examples/SampleApp/package.json b/examples/SampleApp/package.json
index eb08fce2bb..bc555889ab 100644
--- a/examples/SampleApp/package.json
+++ b/examples/SampleApp/package.json
@@ -51,7 +51,6 @@
"react-native-gesture-handler": "^2.26.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-image-picker": "^8.2.1",
- "react-native-keyboard-controller": "^1.20.2",
"react-native-maps": "1.20.1",
"react-native-nitro-modules": "^0.31.3",
"react-native-nitro-sound": "^0.2.9",
diff --git a/examples/SampleApp/src/screens/ChannelScreen.tsx b/examples/SampleApp/src/screens/ChannelScreen.tsx
index c1e35f34f9..333f5d079e 100644
--- a/examples/SampleApp/src/screens/ChannelScreen.tsx
+++ b/examples/SampleApp/src/screens/ChannelScreen.tsx
@@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useState } from 'react';
import type { LocalMessage, Channel as StreamChatChannel } from 'stream-chat';
-import { useHeaderHeight } from '@react-navigation/elements';
import { RouteProp, useFocusEffect, useNavigation } from '@react-navigation/native';
import {
Channel,
@@ -20,7 +19,7 @@ import {
ChannelAvatar,
useChannelPreviewDisplayPresence,
} from 'stream-chat-react-native';
-import { Pressable, StyleSheet, View } from 'react-native';
+import { Platform, Pressable, StyleSheet, View } from 'react-native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useAppContext } from '../context/AppContext';
@@ -230,7 +229,6 @@ export const ChannelScreen: React.FC = ({
},
[chatClient, colors, t, handleMessageInfo],
);
- const headerHeight = useHeaderHeight();
if (!channel || !chatClient) {
return null;
@@ -247,7 +245,7 @@ export const ChannelScreen: React.FC = ({
disableTypingIndicator
enforceUniqueReaction
initialScrollToFirstUnreadMessage
- keyboardVerticalOffset={headerHeight}
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : -300}
messageActions={messageActions}
MessageHeader={MessageReminderHeader}
MessageLocation={MessageLocation}
diff --git a/examples/SampleApp/src/screens/ThreadScreen.tsx b/examples/SampleApp/src/screens/ThreadScreen.tsx
index 5abc0b6073..4bea7bc519 100644
--- a/examples/SampleApp/src/screens/ThreadScreen.tsx
+++ b/examples/SampleApp/src/screens/ThreadScreen.tsx
@@ -1,6 +1,5 @@
import React, { useCallback } from 'react';
-import { StyleSheet, View } from 'react-native';
-import { useHeaderHeight } from '@react-navigation/elements';
+import { Platform, StyleSheet, View } from 'react-native';
import {
Channel,
@@ -86,8 +85,7 @@ export const ThreadScreen: React.FC = ({
const { client: chatClient } = useChatContext();
const { t } = useTranslationContext();
const { setThread } = useStreamChatContext();
- const { messageInputFloating } = useAppContext();
- const headerHeight = useHeaderHeight();
+ const { messageInputFloating, messageListImplementation } = useAppContext();
const onPressMessage: NonNullable['onPressMessage']> = (
payload,
@@ -125,7 +123,7 @@ export const ThreadScreen: React.FC = ({
AttachmentPickerSelectionBar={CustomAttachmentPickerSelectionBar}
channel={channel}
enforceUniqueReaction
- keyboardVerticalOffset={headerHeight}
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : -300}
messageActions={messageActions}
messageInputFloating={messageInputFloating}
MessageHeader={MessageReminderHeader}
@@ -135,7 +133,7 @@ export const ThreadScreen: React.FC = ({
threadList
>
-
+
);
diff --git a/examples/SampleApp/yarn.lock b/examples/SampleApp/yarn.lock
index a93301a6f6..3f241a37a9 100644
--- a/examples/SampleApp/yarn.lock
+++ b/examples/SampleApp/yarn.lock
@@ -1580,10 +1580,10 @@
"@gorhom/portal" "1.0.14"
invariant "^2.2.4"
-"@gorhom/bottom-sheet@^5.1.8":
- version "5.1.8"
- resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-5.1.8.tgz#65547917f5b1dae5a1291dabd4ea8bfee09feba4"
- integrity sha512-QuYIVjn3K9bW20n5bgOSjvxBYoWG4YQXiLGOheEAMgISuoT6sMcA270ViSkkb0fenPxcIOwzCnFNuxmr739T9A==
+"@gorhom/bottom-sheet@^5.2.8":
+ version "5.2.8"
+ resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-5.2.8.tgz#25e49122c30ffe83d3813b3bcf3dec39f3359aeb"
+ integrity sha512-+N27SMpbBxXZQ/IA2nlEV6RGxL/qSFHKfdFKcygvW+HqPG5jVNb1OqehLQsGfBP+Up42i0gW5ppI+DhpB7UCzA==
dependencies:
"@gorhom/portal" "1.0.14"
invariant "^2.2.4"
@@ -7658,13 +7658,6 @@ react-native-is-edge-to-edge@^1.2.1:
resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358"
integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==
-react-native-keyboard-controller@^1.20.2:
- version "1.20.2"
- resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.20.2.tgz#2953341f48e25fec20dd732241cb8152251fd4d1"
- integrity sha512-3xvPTIfasAbosDxT3Mc6b5Xr/M+yq99ECCM4iGnSAngziIVUZsZuPpfYL7nN1UiN9rQjWKvjdul/jq9E0V1s2w==
- dependencies:
- react-native-is-edge-to-edge "^1.2.1"
-
react-native-lightbox@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/react-native-lightbox/-/react-native-lightbox-0.7.0.tgz#e52b4d7fcc141f59d7b23f0180de535e35b20ec9"
diff --git a/package/src/components/KeyboardCompatibleView/KeyboardControllerAvoidingView.tsx b/package/src/components/KeyboardCompatibleView/KeyboardControllerAvoidingView.tsx
index f75e5853b7..b535c2680f 100644
--- a/package/src/components/KeyboardCompatibleView/KeyboardControllerAvoidingView.tsx
+++ b/package/src/components/KeyboardCompatibleView/KeyboardControllerAvoidingView.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import {
Keyboard,
@@ -6,42 +6,40 @@ import {
KeyboardAvoidingViewProps as ReactNativeKeyboardAvoidingViewProps,
} from 'react-native';
-import {
- KeyboardAvoidingView as KeyboardControllerPackageKeyboardAvoidingView,
- KeyboardController as KeyboardControllerPackageKeyboardController,
- KeyboardEvents,
- KeyboardProvider,
-} from 'react-native-keyboard-controller';
-
import { KeyboardCompatibleView as KeyboardCompatibleViewDefault } from './KeyboardCompatibleView';
type ExtraKeyboardControllerProps = {
behavior?: 'translate-with-padding';
};
+type KeyboardControllerModule = typeof import('react-native-keyboard-controller');
+
+const optionalRequire = (): T | undefined => {
+ try {
+ return require('react-native-keyboard-controller') as T;
+ } catch {
+ return undefined;
+ }
+};
+
export type KeyboardCompatibleViewProps = ReactNativeKeyboardAvoidingViewProps &
ExtraKeyboardControllerProps;
-let KeyboardControllerPackage:
- | {
- KeyboardAvoidingView: typeof KeyboardControllerPackageKeyboardAvoidingView;
- KeyboardController: typeof KeyboardControllerPackageKeyboardController;
- KeyboardProvider: typeof KeyboardProvider;
- KeyboardEvents: typeof KeyboardEvents;
- }
- | undefined;
+const KeyboardControllerPackage = optionalRequire();
-try {
- KeyboardControllerPackage = require('react-native-keyboard-controller');
-} catch (e) {
- KeyboardControllerPackage = undefined;
-}
+const { AndroidSoftInputModes, KeyboardController, KeyboardProvider, KeyboardAvoidingView } =
+ KeyboardControllerPackage ?? {};
export const KeyboardCompatibleView = (props: KeyboardCompatibleViewProps) => {
const { behavior = 'translate-with-padding', children, ...rest } = props;
- const KeyboardProvider = KeyboardControllerPackage?.KeyboardProvider;
- const KeyboardAvoidingView = KeyboardControllerPackage?.KeyboardAvoidingView;
+ useEffect(() => {
+ if (AndroidSoftInputModes) {
+ KeyboardController?.setInputMode(AndroidSoftInputModes.SOFT_INPUT_ADJUST_RESIZE);
+ }
+
+ return () => KeyboardController?.setDefaultMode();
+ }, []);
if (KeyboardProvider && KeyboardAvoidingView) {
return (
diff --git a/package/src/components/Message/MessageSimple/MessageWrapper.tsx b/package/src/components/Message/MessageSimple/MessageWrapper.tsx
index 39a4a36f22..97da35634e 100644
--- a/package/src/components/Message/MessageSimple/MessageWrapper.tsx
+++ b/package/src/components/Message/MessageSimple/MessageWrapper.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback } from 'react';
+import React from 'react';
import { View } from 'react-native';
@@ -14,7 +14,6 @@ import { ThemeProvider, useTheme } from '../../../contexts/themeContext/ThemeCon
import { useStateStore } from '../../../hooks/useStateStore';
import { ChannelUnreadStateStoreType } from '../../../state-store/channel-unread-state';
-import { MessagePreviousAndNextMessageStoreType } from '../../../state-store/message-list-prev-next-state';
const channelUnreadStateSelector = (state: ChannelUnreadStateStoreType) => ({
first_unread_message_id: state.channelUnreadState?.first_unread_message_id,
@@ -25,10 +24,12 @@ const channelUnreadStateSelector = (state: ChannelUnreadStateStoreType) => ({
export type MessageWrapperProps = {
message: LocalMessage;
+ previousMessage?: LocalMessage;
+ nextMessage?: LocalMessage;
};
export const MessageWrapper = React.memo((props: MessageWrapperProps) => {
- const { message } = props;
+ const { message, previousMessage, nextMessage } = props;
const { client } = useChatContext();
const {
channelUnreadStateStore,
@@ -47,34 +48,22 @@ export const MessageWrapper = React.memo((props: MessageWrapperProps) => {
myMessageTheme,
shouldShowUnreadUnderlay,
} = useMessagesContext();
- const {
- goToMessage,
- onThreadSelect,
- noGroupByUser,
- modifiedTheme,
- messageListPreviousAndNextMessageStore,
- } = useMessageListItemContext();
+ const { goToMessage, onThreadSelect, noGroupByUser, modifiedTheme } = useMessageListItemContext();
const dateSeparatorDate = useMessageDateSeparator({
hideDateSeparators,
message,
- messageListPreviousAndNextMessageStore,
+ previousMessage,
});
- const selector = useCallback(
- (state: MessagePreviousAndNextMessageStoreType) => ({
- nextMessage: state.messageList[message.id]?.nextMessage,
- }),
- [message.id],
- );
- const { nextMessage } = useStateStore(messageListPreviousAndNextMessageStore.state, selector);
const isNewestMessage = nextMessage === undefined;
const groupStyles = useMessageGroupStyles({
dateSeparatorDate,
getMessageGroupStyle,
maxTimeBetweenGroupedMessages,
message,
- messageListPreviousAndNextMessageStore,
+ previousMessage,
+ nextMessage,
noGroupByUser,
});
diff --git a/package/src/components/Message/MessageSimple/__tests__/__snapshots__/MessageAvatar.test.js.snap b/package/src/components/Message/MessageSimple/__tests__/__snapshots__/MessageAvatar.test.js.snap
index 22a0a2318a..cfea0def75 100644
--- a/package/src/components/Message/MessageSimple/__tests__/__snapshots__/MessageAvatar.test.js.snap
+++ b/package/src/components/Message/MessageSimple/__tests__/__snapshots__/MessageAvatar.test.js.snap
@@ -36,12 +36,16 @@ exports[`MessageAvatar should render message avatar 1`] = `
{
"backgroundColor": undefined,
},
- undefined,
+ {
+ "borderColor": "hsla(0, 0%, 0%, 0.1)",
+ "borderWidth": 1,
+ },
]
}
testID="avatar-image"
>
{
return 'generic-message';
};
-const renderItem = ({ item: message }: { item: LocalMessage }) => {
- return ;
-};
-
const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) => {
const LoadingMoreRecentIndicator = props.threadList
? InlineLoadingMoreRecentThreadIndicator
@@ -365,17 +361,27 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
[myMessageThemeString, theme],
);
- const {
- messageListPreviousAndNextMessageStore,
- processedMessageList,
- rawMessageList,
- viewabilityChangedCallback,
- } = useMessageList({
+ const { processedMessageList, rawMessageList, viewabilityChangedCallback } = useMessageList({
isFlashList: true,
isLiveStreaming,
threadList,
});
+ const renderItem = useCallback(
+ ({ item: message, index }: { item: LocalMessage; index: number }) => {
+ const previousMessage = processedMessageList[index - 1];
+ const nextMessage = processedMessageList[index + 1];
+ return (
+
+ );
+ },
+ [processedMessageList],
+ );
+
/**
* We need topMessage and channelLastRead values to set the initial scroll position.
* So these values only get used if `initialScrollToFirstUnreadMessage` prop is true.
@@ -742,20 +748,12 @@ const MessageFlashListWithContext = (props: MessageFlashListPropsWithContext) =>
const messageListItemContextValue: MessageListItemContextValue = useMemo(
() => ({
goToMessage,
- messageListPreviousAndNextMessageStore,
modifiedTheme,
noGroupByUser,
onThreadSelect,
setNativeScrollability,
}),
- [
- goToMessage,
- messageListPreviousAndNextMessageStore,
- modifiedTheme,
- noGroupByUser,
- onThreadSelect,
- setNativeScrollability,
- ],
+ [goToMessage, modifiedTheme, noGroupByUser, onThreadSelect, setNativeScrollability],
);
/**
diff --git a/package/src/components/MessageList/MessageList.tsx b/package/src/components/MessageList/MessageList.tsx
index 3cdc53ba94..fbd75727ea 100644
--- a/package/src/components/MessageList/MessageList.tsx
+++ b/package/src/components/MessageList/MessageList.tsx
@@ -248,10 +248,6 @@ type MessageListPropsWithContext = Pick<
isLiveStreaming?: boolean;
};
-const renderItem = ({ item: message }: { item: LocalMessage }) => {
- return ;
-};
-
const messageInputHeightStoreSelector = (state: MessageInputHeightState) => ({
height: state.height,
});
@@ -349,15 +345,29 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
* NOTE: rawMessageList changes only when messages array state changes
* processedMessageList changes on any state change
*/
- const {
- messageListPreviousAndNextMessageStore,
- processedMessageList,
- rawMessageList,
- viewabilityChangedCallback,
- } = useMessageList({
+ const { processedMessageList, rawMessageList, viewabilityChangedCallback } = useMessageList({
isLiveStreaming,
threadList,
});
+
+ const processedMessageListRef = useRef(processedMessageList);
+ processedMessageListRef.current = processedMessageList;
+
+ const renderItem = useCallback(
+ ({ item: message, index }: { item: LocalMessage; index: number }) => {
+ const previousMessage = processedMessageListRef.current[index + 1];
+ const nextMessage = processedMessageListRef.current[index - 1];
+ return (
+
+ );
+ },
+ [processedMessageListRef],
+ );
+
const messageListLengthBeforeUpdate = useRef(0);
const messageListLengthAfterUpdate = processedMessageList.length;
@@ -805,20 +815,12 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
const messageListItemContextValue: MessageListItemContextValue = useMemo(
() => ({
goToMessage,
- messageListPreviousAndNextMessageStore,
modifiedTheme,
noGroupByUser,
onThreadSelect,
setNativeScrollability,
}),
- [
- goToMessage,
- messageListPreviousAndNextMessageStore,
- modifiedTheme,
- noGroupByUser,
- onThreadSelect,
- setNativeScrollability,
- ],
+ [goToMessage, modifiedTheme, noGroupByUser, onThreadSelect, setNativeScrollability],
);
/**
@@ -1122,6 +1124,7 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
);
}
+ // TODO: Make sure this is actually overridable as the previous FlatList was.
return (
{
) : (
-
+ // TODO: Consider hiding this behind a feature flag.
+ layout={LinearTransition.duration(200)}
contentContainerStyle={flatListContentContainerStyle}
/** Disables the MessageList UI. Which means, message actions, reactions won't work. */
data={processedMessageList}
@@ -1159,11 +1164,11 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
onScrollToIndexFailed={onScrollToIndexFailedRef.current}
onTouchEnd={dismissImagePicker}
onViewableItemsChanged={stableOnViewableItemsChanged}
+ // @ts-expect-error Safe to do for now
ref={refCallback}
renderItem={renderItem}
scrollEventThrottle={isLiveStreaming ? 16 : undefined}
showsVerticalScrollIndicator={false}
- // @ts-expect-error react-native internal
strictMode={isLiveStreaming}
style={flatListStyle}
testID='message-flat-list'
diff --git a/package/src/components/MessageList/__tests__/useMessageDateSeparator.test.ts b/package/src/components/MessageList/__tests__/useMessageDateSeparator.test.ts
index 07203353a6..901ea76a8c 100644
--- a/package/src/components/MessageList/__tests__/useMessageDateSeparator.test.ts
+++ b/package/src/components/MessageList/__tests__/useMessageDateSeparator.test.ts
@@ -2,15 +2,12 @@ import { renderHook } from '@testing-library/react-native';
import { LocalMessage } from 'stream-chat';
-import { MessagePreviousAndNextMessageStore } from '../../../state-store/message-list-prev-next-state';
import { useMessageDateSeparator } from '../hooks/useMessageDateSeparator';
describe('useMessageDateSeparator', () => {
- let messageListPreviousAndNextMessageStore: MessagePreviousAndNextMessageStore;
let messages: LocalMessage[];
beforeEach(() => {
- messageListPreviousAndNextMessageStore = new MessagePreviousAndNextMessageStore();
messages = [
{
created_at: new Date('2020-01-01T00:00:00.000Z'),
@@ -28,13 +25,10 @@ describe('useMessageDateSeparator', () => {
text: 'Hello World',
},
] as LocalMessage[];
- messageListPreviousAndNextMessageStore.setMessageListPreviousAndNextMessage({ messages });
});
it('should return undefined if no message is passed', () => {
- const { result } = renderHook(() =>
- useMessageDateSeparator({ message: undefined, messageListPreviousAndNextMessageStore }),
- );
+ const { result } = renderHook(() => useMessageDateSeparator({ message: undefined }));
expect(result.current).toBeUndefined();
});
@@ -43,7 +37,7 @@ describe('useMessageDateSeparator', () => {
useMessageDateSeparator({
hideDateSeparators: true,
message: messages[1],
- messageListPreviousAndNextMessageStore,
+ previousMessage: messages[0],
}),
);
expect(result.current).toBeUndefined();
@@ -51,7 +45,7 @@ describe('useMessageDateSeparator', () => {
it('should return the date separator for a message if previous message is not the same day', () => {
const { result } = renderHook(() =>
- useMessageDateSeparator({ message: messages[1], messageListPreviousAndNextMessageStore }),
+ useMessageDateSeparator({ message: messages[1], previousMessage: messages[0] }),
);
expect(result.current).toBe(messages[1].created_at);
});
@@ -69,14 +63,12 @@ describe('useMessageDateSeparator', () => {
text: 'World',
},
] as LocalMessage[];
- const messageListPreviousAndNextMessageStore = new MessagePreviousAndNextMessageStore();
- messageListPreviousAndNextMessageStore.setMessageListPreviousAndNextMessage({ messages });
const { result: resultOfFirstMessage } = renderHook(() =>
- useMessageDateSeparator({ message: messages[0], messageListPreviousAndNextMessageStore }),
+ useMessageDateSeparator({ message: messages[0], previousMessage: undefined }),
);
expect(resultOfFirstMessage.current).toBe(messages[0].created_at);
const { result: resultOfSecondMessage } = renderHook(() =>
- useMessageDateSeparator({ message: messages[1], messageListPreviousAndNextMessageStore }),
+ useMessageDateSeparator({ message: messages[1], previousMessage: messages[0] }),
);
expect(resultOfSecondMessage.current).toBeUndefined();
});
diff --git a/package/src/components/MessageList/hooks/useMessageDateSeparator.ts b/package/src/components/MessageList/hooks/useMessageDateSeparator.ts
index 80957b0b1a..32433fa936 100644
--- a/package/src/components/MessageList/hooks/useMessageDateSeparator.ts
+++ b/package/src/components/MessageList/hooks/useMessageDateSeparator.ts
@@ -1,13 +1,7 @@
-import { useCallback, useMemo } from 'react';
+import { useMemo } from 'react';
import { LocalMessage } from 'stream-chat';
-import { useStateStore } from '../../../hooks/useStateStore';
-import {
- MessagePreviousAndNextMessageStore,
- MessagePreviousAndNextMessageStoreType,
-} from '../../../state-store/message-list-prev-next-state';
-
export const getDateSeparatorValue = ({
hideDateSeparators,
message,
@@ -37,20 +31,12 @@ export const getDateSeparatorValue = ({
export const useMessageDateSeparator = ({
hideDateSeparators,
message,
- messageListPreviousAndNextMessageStore,
+ previousMessage,
}: {
hideDateSeparators?: boolean;
message?: LocalMessage;
- messageListPreviousAndNextMessageStore: MessagePreviousAndNextMessageStore;
+ previousMessage?: LocalMessage;
}) => {
- const selector = useCallback(
- (state: MessagePreviousAndNextMessageStoreType) => ({
- previousMessage: message ? state.messageList[message.id]?.previousMessage : undefined,
- }),
- [message],
- );
- const { previousMessage } = useStateStore(messageListPreviousAndNextMessageStore.state, selector);
-
const dateSeparatorDate = useMemo(() => {
if (!message && !previousMessage) {
return undefined;
diff --git a/package/src/components/MessageList/hooks/useMessageGroupStyles.ts b/package/src/components/MessageList/hooks/useMessageGroupStyles.ts
index 4d4d286bc8..43378ff6420 100644
--- a/package/src/components/MessageList/hooks/useMessageGroupStyles.ts
+++ b/package/src/components/MessageList/hooks/useMessageGroupStyles.ts
@@ -1,15 +1,10 @@
-import { useCallback, useMemo } from 'react';
+import { useMemo } from 'react';
import { LocalMessage } from 'stream-chat';
import { useMessageDateSeparator } from './useMessageDateSeparator';
import { MessagesContextValue } from '../../../contexts/messagesContext/MessagesContext';
-import { useStateStore } from '../../../hooks/useStateStore';
-import {
- MessagePreviousAndNextMessageStore,
- MessagePreviousAndNextMessageStoreType,
-} from '../../../state-store/message-list-prev-next-state';
import { getGroupStyle } from '../utils/getGroupStyles';
/**
@@ -20,7 +15,8 @@ export const useMessageGroupStyles = ({
dateSeparatorDate,
maxTimeBetweenGroupedMessages,
message,
- messageListPreviousAndNextMessageStore,
+ previousMessage,
+ nextMessage,
getMessageGroupStyle = getGroupStyle,
}: {
noGroupByUser?: boolean;
@@ -28,24 +24,13 @@ export const useMessageGroupStyles = ({
dateSeparatorDate?: Date;
maxTimeBetweenGroupedMessages?: number;
message: LocalMessage;
- messageListPreviousAndNextMessageStore: MessagePreviousAndNextMessageStore;
+ previousMessage?: LocalMessage;
+ nextMessage?: LocalMessage;
}) => {
- const selector = useCallback(
- (state: MessagePreviousAndNextMessageStoreType) => ({
- nextMessage: state.messageList[message.id]?.nextMessage,
- previousMessage: state.messageList[message.id]?.previousMessage,
- }),
- [message.id],
- );
- const { previousMessage, nextMessage } = useStateStore(
- messageListPreviousAndNextMessageStore.state,
- selector,
- );
-
// This is needed to calculate the group styles for the next message
const nextMessageDateSeparatorDate = useMessageDateSeparator({
message: nextMessage,
- messageListPreviousAndNextMessageStore,
+ previousMessage: message,
});
const groupStyles = useMemo(() => {
diff --git a/package/src/components/MessageList/hooks/useMessageList.ts b/package/src/components/MessageList/hooks/useMessageList.ts
index e5ee25fe65..61d129238c 100644
--- a/package/src/components/MessageList/hooks/useMessageList.ts
+++ b/package/src/components/MessageList/hooks/useMessageList.ts
@@ -1,4 +1,4 @@
-import { useEffect, useMemo, useState } from 'react';
+import { useMemo } from 'react';
import type { LocalMessage } from 'stream-chat';
@@ -11,7 +11,6 @@ import { usePaginatedMessageListContext } from '../../../contexts/paginatedMessa
import { useThreadContext } from '../../../contexts/threadContext/ThreadContext';
import { useRAFCoalescedValue } from '../../../hooks';
-import { MessagePreviousAndNextMessageStore } from '../../../state-store/message-list-prev-next-state';
export type UseMessageListParams = {
threadList?: boolean;
@@ -60,9 +59,6 @@ export const useMessageList = (params: UseMessageListParams) => {
const { messages, viewabilityChangedCallback } = usePaginatedMessageListContext();
const { threadMessages } = useThreadContext();
const messageList = threadList ? threadMessages : messages;
- const [messageListPreviousAndNextMessageStore] = useState(
- () => new MessagePreviousAndNextMessageStore(),
- );
const processedMessageList = useMemo(() => {
const newMessageList = [];
@@ -84,24 +80,16 @@ export const useMessageList = (params: UseMessageListParams) => {
return newMessageList;
}, [messageList, deletedMessagesVisibilityType, client.userID, isFlashList]);
- useEffect(() => {
- messageListPreviousAndNextMessageStore.setMessageListPreviousAndNextMessage({
- isFlashList,
- messages: processedMessageList,
- });
- }, [processedMessageList, messageListPreviousAndNextMessageStore, isFlashList]);
-
const data = useRAFCoalescedValue(processedMessageList, isLiveStreaming);
return useMemo(
() => ({
- messageListPreviousAndNextMessageStore,
/** Messages enriched with dates/readby/groups and also reversed in order */
processedMessageList: data,
/** Raw messages from the channel state */
rawMessageList: messageList,
viewabilityChangedCallback,
}),
- [data, messageList, messageListPreviousAndNextMessageStore, viewabilityChangedCallback],
+ [data, messageList, viewabilityChangedCallback],
);
};
diff --git a/package/src/components/MessageList/utils/getGroupStyles.ts b/package/src/components/MessageList/utils/getGroupStyles.ts
index e3dec9e16a..06e704e678 100644
--- a/package/src/components/MessageList/utils/getGroupStyles.ts
+++ b/package/src/components/MessageList/utils/getGroupStyles.ts
@@ -4,8 +4,8 @@ import { isEditedMessage } from '../../../utils/utils';
export type MessageGroupStylesParams = {
message: LocalMessage;
- previousMessage: LocalMessage;
- nextMessage: LocalMessage;
+ previousMessage?: LocalMessage;
+ nextMessage?: LocalMessage;
maxTimeBetweenGroupedMessages?: number;
dateSeparatorDate?: Date;
nextMessageDateSeparatorDate?: Date;
diff --git a/package/src/components/Thread/Thread.tsx b/package/src/components/Thread/Thread.tsx
index 93c177ccea..4edc298e1f 100644
--- a/package/src/components/Thread/Thread.tsx
+++ b/package/src/components/Thread/Thread.tsx
@@ -68,6 +68,7 @@ type ThreadPropsWithContext = Pick &
* Call custom function on closing thread if handling thread state elsewhere
*/
onThreadDismount?: () => void;
+ shouldUseFlashList?: boolean;
};
const ThreadWithContext = (props: ThreadPropsWithContext) => {
@@ -86,6 +87,7 @@ const ThreadWithContext = (props: ThreadPropsWithContext) => {
parentMessagePreventPress = true,
thread,
threadInstance,
+ shouldUseFlashList = false,
} = props;
useEffect(() => {
@@ -119,13 +121,13 @@ const ThreadWithContext = (props: ThreadPropsWithContext) => {
[parentMessagePreventPress],
);
- if (!thread) {
+ if (!thread?.id) {
return null;
}
return (
- {FlashList ? (
+ {FlashList && shouldUseFlashList ? (
void;
- /**
- * Store to get the previous and next message in the message list
- */
- messageListPreviousAndNextMessageStore: MessagePreviousAndNextMessageStore;
/**
* Theme to use for the message list item
*/