diff --git a/examples/ExpoMessaging/app/channel/[cid]/index.tsx b/examples/ExpoMessaging/app/channel/[cid]/index.tsx
index c52c8ef231..0410bec2f6 100644
--- a/examples/ExpoMessaging/app/channel/[cid]/index.tsx
+++ b/examples/ExpoMessaging/app/channel/[cid]/index.tsx
@@ -6,14 +6,11 @@ import {
useChatContext,
ThreadContextValue,
MessageList,
- WithComponents,
} from 'stream-chat-expo';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { AuthProgressLoader } from '../../../components/AuthProgressLoader';
import { AppContext } from '../../../context/AppContext';
import { useHeaderHeight } from '@react-navigation/elements';
-import InputButtons from '../../../components/InputButtons';
-import { MessageLocation } from '../../../components/LocationSharing/MessageLocation';
import { StyleSheet, View } from 'react-native';
export default function ChannelScreen() {
@@ -71,23 +68,21 @@ export default function ChannelScreen() {
-
-
- {
- setThread(thread);
- router.push(`/channel/${channel.cid}/thread/${thread?.cid ?? ''}`);
- }}
- />
-
-
-
+
+ {
+ setThread(thread);
+ router.push(`/channel/${channel.cid}/thread/${thread?.cid ?? ''}`);
+ }}
+ />
+
+
);
}
diff --git a/examples/ExpoMessaging/components/ChatWrapper.tsx b/examples/ExpoMessaging/components/ChatWrapper.tsx
index d0bf6eff10..416eadc24d 100644
--- a/examples/ExpoMessaging/components/ChatWrapper.tsx
+++ b/examples/ExpoMessaging/components/ChatWrapper.tsx
@@ -6,6 +6,7 @@ import {
SqliteClient,
Streami18n,
useCreateChatClient,
+ WithComponents,
} from 'stream-chat-expo';
import { UserResponse } from 'stream-chat';
import { AuthProgressLoader } from './AuthProgressLoader';
@@ -13,6 +14,7 @@ import { useStreamChatTheme } from '../hooks/useStreamChatTheme';
import { useUserContext } from '@/context/UserContext';
import { STREAM_API_KEY, USER_TOKENS } from '@/constants/ChatUsers';
import { usePushNotifications } from '@/hooks/usePushNotifications';
+import { useExpoMessagingComponentOverrides } from './ExpoMessagingComponentOverrides';
import '../utils/backgroundMessageHandler';
const streami18n = new Streami18n({
@@ -39,16 +41,19 @@ export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => {
});
const theme = useStreamChatTheme();
+ const componentOverrides = useExpoMessagingComponentOverrides();
if (!chatClient) {
return ;
}
return (
-
-
- {children}
-
-
+
+
+
+ {children}
+
+
+
);
};
diff --git a/examples/ExpoMessaging/components/ExpoMessagingComponentOverrides.tsx b/examples/ExpoMessaging/components/ExpoMessagingComponentOverrides.tsx
new file mode 100644
index 0000000000..1f18c3703d
--- /dev/null
+++ b/examples/ExpoMessaging/components/ExpoMessagingComponentOverrides.tsx
@@ -0,0 +1,12 @@
+import { useMemo } from 'react';
+import type { ComponentOverrides } from 'stream-chat-expo';
+
+import InputButtons from './InputButtons';
+import { MessageLocation } from './LocationSharing/MessageLocation';
+
+export const useExpoMessagingComponentOverrides = () => {
+ return useMemo(
+ () => ({ InputButtons, MessageLocation }),
+ [],
+ );
+};
diff --git a/examples/SampleApp/App.tsx b/examples/SampleApp/App.tsx
index 474491377d..046b44170c 100644
--- a/examples/SampleApp/App.tsx
+++ b/examples/SampleApp/App.tsx
@@ -4,15 +4,12 @@ import {
I18nManager,
LogBox,
Platform,
- StyleSheet,
useColorScheme,
- View,
} from 'react-native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { SafeAreaProvider } from 'react-native-safe-area-context';
-import { BlurView } from '@react-native-community/blur';
import {
Chat,
createTextComposerEmojiMiddleware,
@@ -23,7 +20,7 @@ import {
Streami18n,
ThemeProvider,
useOverlayContext,
- useTheme,
+ WithComponents,
} from 'stream-chat-react-native';
import { getMessaging } from '@react-native-firebase/messaging';
@@ -55,10 +52,10 @@ import Geolocation from '@react-native-community/geolocation';
import type { StackNavigatorParamList, UserSelectorParamList } from './src/types';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { navigateToChannel, RootNavigationRef } from './src/utils/RootNavigation';
-import FastImage from 'react-native-fast-image';
import { StreamChatProvider } from './src/context/StreamChatContext';
import { MapScreen } from './src/screens/MapScreen';
import { watchLocation } from './src/utils/watchLocation';
+import { useSampleAppComponentOverrides } from './src/components/SampleAppComponentOverrides';
Geolocation.setRNConfiguration({
skipPermissionRequests: false,
@@ -107,30 +104,6 @@ const Stack = createNativeStackNavigator();
const UserSelectorStack = createNativeStackNavigator();
const RTL_STORAGE_KEY = '@stream-rn-sampleapp-rtl-enabled';
-const MessageOverlayBlurBackground = () => {
- const {
- theme: { semantics },
- } = useTheme();
- const scheme = useColorScheme();
- const isDark = scheme === 'dark';
- const isIOS = Platform.OS === 'ios';
-
- return (
- <>
-
-
- >
- );
-};
-
const App = () => {
const { chatClient, isConnecting, loginUser, logout, switchUser } = useChatClient();
const [rtlEnabled, setRtlEnabled] = useState(undefined);
@@ -152,6 +125,7 @@ const App = () => {
const colorScheme = useColorScheme();
const streamChatTheme = useStreamChatTheme();
const streami18n = new Streami18n();
+ const componentOverrides = useSampleAppComponentOverrides(messageOverlayBackdrop);
const setRTLEnabled = React.useCallback(async (enabled: boolean) => {
await AsyncStore.setItem(RTL_STORAGE_KEY, enabled);
@@ -295,50 +269,46 @@ const App = () => {
}}
>
-
-
-
-
+
+
+
- {isConnecting && !chatClient ? (
-
- ) : chatClient ? (
-
- ) : (
-
- )}
-
-
-
-
+
+ {isConnecting && !chatClient ? (
+
+ ) : chatClient ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
);
@@ -369,8 +339,6 @@ const DrawerNavigatorWrapper: React.FC<{
diff --git a/examples/SampleApp/src/components/ChannelPreview.tsx b/examples/SampleApp/src/components/ChannelPreview.tsx
index 2146d9c11d..81ff28f77a 100644
--- a/examples/SampleApp/src/components/ChannelPreview.tsx
+++ b/examples/SampleApp/src/components/ChannelPreview.tsx
@@ -1,8 +1,6 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import {
- ChannelPreviewView,
- ChannelPreviewViewProps,
ChannelPreviewStatus,
ChannelPreviewStatusProps,
Pin,
@@ -34,7 +32,7 @@ const styles = StyleSheet.create({
},
});
-const CustomChannelPreviewStatus = (props: ChannelPreviewStatusProps) => {
+export const CustomChannelPreviewStatus = (props: ChannelPreviewStatusProps) => {
const { channel } = props;
const membership = useChannelMembershipState(channel);
@@ -53,7 +51,3 @@ const CustomChannelPreviewStatus = (props: ChannelPreviewStatusProps) => {
);
};
-
-export const ChannelPreview: React.FC = (props) => {
- return ;
-};
diff --git a/examples/SampleApp/src/components/SampleAppComponentOverrides.tsx b/examples/SampleApp/src/components/SampleAppComponentOverrides.tsx
new file mode 100644
index 0000000000..d5ec67d778
--- /dev/null
+++ b/examples/SampleApp/src/components/SampleAppComponentOverrides.tsx
@@ -0,0 +1,60 @@
+import React, { useMemo } from 'react';
+import { Platform, StyleSheet, useColorScheme, View } from 'react-native';
+import type { ComponentOverrides } from 'stream-chat-react-native';
+import { BlurView } from '@react-native-community/blur';
+import FastImage from 'react-native-fast-image';
+import {
+ useTheme,
+} from 'stream-chat-react-native';
+
+import { CustomAttachmentPickerContent } from './AttachmentPickerContent';
+import { CustomChannelPreviewStatus } from './ChannelPreview';
+import { MessageLocation } from './LocationSharing/MessageLocation';
+import type { MessageOverlayBackdropConfigItem } from './SecretMenu';
+
+const MessageOverlayBlurBackground: NonNullable =
+ () => {
+ const {
+ theme: { semantics },
+ } = useTheme();
+ const scheme = useColorScheme();
+ const isDark = scheme === 'dark';
+ const isIOS = Platform.OS === 'ios';
+
+ return (
+ <>
+
+
+ >
+ );
+ };
+
+const RenderNull = () => null;
+
+export const useSampleAppComponentOverrides = (
+ messageOverlayBackdrop?: MessageOverlayBackdropConfigItem['value'],
+) =>
+ useMemo(
+ () => ({
+ AttachmentPickerContent: CustomAttachmentPickerContent,
+ ChannelListHeaderNetworkDownIndicator: RenderNull,
+ ImageComponent: FastImage,
+ MessageLocation,
+ NetworkDownIndicator: RenderNull,
+ ChannelPreviewStatus: CustomChannelPreviewStatus,
+ ...(messageOverlayBackdrop === 'blurview'
+ ? { MessageOverlayBackground: MessageOverlayBlurBackground }
+ : {}),
+ }),
+ [messageOverlayBackdrop],
+ );
diff --git a/examples/SampleApp/src/screens/ChannelListScreen.tsx b/examples/SampleApp/src/screens/ChannelListScreen.tsx
index 5408e6438a..8053615deb 100644
--- a/examples/SampleApp/src/screens/ChannelListScreen.tsx
+++ b/examples/SampleApp/src/screens/ChannelListScreen.tsx
@@ -10,9 +10,8 @@ import {
View,
} from 'react-native';
import { useNavigation, useScrollToTop } from '@react-navigation/native';
-import { ChannelList, useTheme, useStableCallback, ChannelActionItem, WithComponents } from 'stream-chat-react-native';
+import { ChannelList, useTheme, useStableCallback, ChannelActionItem } from 'stream-chat-react-native';
import { Channel } from 'stream-chat';
-import { ChannelPreview } from '../components/ChannelPreview';
import { ChatScreenHeader } from '../components/ChatScreenHeader';
import { MessageSearchList } from '../components/MessageSearch/MessageSearchList';
import { useAppContext } from '../context/AppContext';
@@ -75,8 +74,6 @@ const options = {
message_limit: 25,
};
-const HeaderNetworkDownIndicator = () => null;
-
export const ChannelListScreen: React.FC = () => {
const { chatClient } = useAppContext();
const navigation = useNavigation();
@@ -249,23 +246,16 @@ export const ChannelListScreen: React.FC = () => {
)}
-
-
-
+
diff --git a/examples/SampleApp/src/screens/ChannelScreen.tsx b/examples/SampleApp/src/screens/ChannelScreen.tsx
index 025f31038e..f423215596 100644
--- a/examples/SampleApp/src/screens/ChannelScreen.tsx
+++ b/examples/SampleApp/src/screens/ChannelScreen.tsx
@@ -17,7 +17,6 @@ import {
MessageActionsParams,
ChannelAvatar,
PortalWhileClosingView,
- WithComponents,
} from 'stream-chat-react-native';
import { Pressable, StyleSheet, View } from 'react-native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
@@ -30,11 +29,9 @@ import type { StackNavigatorParamList } from '../types';
import { NetworkDownIndicator } from '../components/NetworkDownIndicator';
import { useCreateDraftFocusEffect } from '../utils/useCreateDraftFocusEffect.tsx';
import { channelMessageActions } from '../utils/messageActions.tsx';
-import { MessageLocation } from '../components/LocationSharing/MessageLocation.tsx';
import { useStreamChatContext } from '../context/StreamChatContext.tsx';
// import { CustomAttachmentPickerSelectionBar } from '../components/AttachmentPickerSelectionBar.tsx';
import { MessageInfoBottomSheet } from '../components/MessageInfoBottomSheet.tsx';
-import { CustomAttachmentPickerContent } from '../components/AttachmentPickerContent.tsx';
import { ThreadType } from 'stream-chat-react-native-core';
export type ChannelScreenNavigationProp = NativeStackNavigationProp<
@@ -266,14 +263,6 @@ export const ChannelScreen: React.FC = ({ navigation, route
return (
- null,
- }}
- >
= ({ navigation, route
/>
)}
-
);
};
diff --git a/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx b/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx
index cb83e70aa0..192f094e92 100644
--- a/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx
+++ b/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx
@@ -8,7 +8,6 @@ import {
MessageList,
UserAdd,
useTheme,
- WithComponents,
} from 'stream-chat-react-native';
import { User } from '../icons/User';
@@ -23,7 +22,6 @@ import { useLegacyColors } from '../theme/useLegacyColors';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { Channel as StreamChatChannel } from 'stream-chat';
-import { NewDirectMessagingSendButton } from '../components/NewDirectMessagingSendButton';
import type { StackNavigatorParamList } from '../types';
import { Group } from '../icons/Group';
@@ -85,7 +83,7 @@ const styles = StyleSheet.create({
},
});
-const EmptyMessagesIndicator = () => {
+export const EmptyMessagesIndicator = () => {
const { grey } = useLegacyColors();
return (
@@ -339,37 +337,30 @@ export const NewDirectMessagingScreen: React.FC =
},
]}
>
- {
+ setFocusOnMessageInput(true);
+ setFocusOnSearchInput(false);
+ if (messageInputRef.current) {
+ messageInputRef.current.focus();
+ }
+ },
}}
+ audioRecordingEnabled={true}
+ channel={currentChannel.current}
+ enforceUniqueReaction
+ keyboardVerticalOffset={0}
+ onChangeText={setMessageInputText}
+ overrideOwnCapabilities={{ sendMessage: true }}
+ setInputRef={(ref) => (messageInputRef.current = ref)}
>
- {
- setFocusOnMessageInput(true);
- setFocusOnSearchInput(false);
- if (messageInputRef.current) {
- messageInputRef.current.focus();
- }
- },
- }}
- audioRecordingEnabled={true}
- channel={currentChannel.current}
- enforceUniqueReaction
- keyboardVerticalOffset={0}
- onChangeText={setMessageInputText}
- overrideOwnCapabilities={{ sendMessage: true }}
- setInputRef={(ref) => (messageInputRef.current = ref)}
- >
- {renderUserSearch({ inSafeArea: true })}
- {results && results.length >= 0 && !focusOnSearchInput && focusOnMessageInput && (
-
- )}
-
-
-
+ {renderUserSearch({ inSafeArea: true })}
+ {results && results.length >= 0 && !focusOnSearchInput && focusOnMessageInput && (
+
+ )}
+
+
);
};
diff --git a/examples/SampleApp/src/screens/SharedGroupsScreen.tsx b/examples/SampleApp/src/screens/SharedGroupsScreen.tsx
index c092f1e97d..263fa22430 100644
--- a/examples/SampleApp/src/screens/SharedGroupsScreen.tsx
+++ b/examples/SampleApp/src/screens/SharedGroupsScreen.tsx
@@ -11,7 +11,6 @@ import {
useTheme,
Avatar,
getInitialsFromName,
- WithComponents,
} from 'stream-chat-react-native';
import { ScreenHeader } from '../components/ScreenHeader';
@@ -57,7 +56,7 @@ const styles = StyleSheet.create({
type CustomPreviewProps = ChannelPreviewViewProps;
-const CustomPreview: React.FC = ({ channel }) => {
+export const SharedGroupsPreview: React.FC = ({ channel }) => {
const { chatClient } = useAppContext();
const name = useChannelPreviewDisplayName(channel, 30);
const navigation = useNavigation>();
@@ -145,7 +144,7 @@ const EmptyListComponent = () => {
};
// Custom empty state that also shows when there's only the 1:1 direct channel
-const SharedGroupsEmptyState = () => {
+export const SharedGroupsEmptyState = () => {
const { channels, loadingChannels, refreshing } = useChannelsContext();
if (loadingChannels || refreshing) {
@@ -179,24 +178,17 @@ export const SharedGroupsScreen: React.FC = ({
return (
-
-
-
+ options={{
+ watch: false,
+ }}
+ sort={{
+ last_updated: -1,
+ }}
+ />
);
};
diff --git a/examples/SampleApp/src/screens/ThreadScreen.tsx b/examples/SampleApp/src/screens/ThreadScreen.tsx
index 8884bf9bc0..cbb5241e4d 100644
--- a/examples/SampleApp/src/screens/ThreadScreen.tsx
+++ b/examples/SampleApp/src/screens/ThreadScreen.tsx
@@ -12,7 +12,6 @@ import {
useTranslationContext,
useTypingString,
PortalWhileClosingView,
- WithComponents,
} from 'stream-chat-react-native';
import { useStateStore } from 'stream-chat-react-native';
@@ -27,7 +26,6 @@ import { channelMessageActions } from '../utils/messageActions.tsx';
import { useStreamChatContext } from '../context/StreamChatContext.tsx';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
// import { CustomAttachmentPickerSelectionBar } from '../components/AttachmentPickerSelectionBar.tsx';
-import { MessageLocation } from '../components/LocationSharing/MessageLocation.tsx';
import { useAppContext } from '../context/AppContext.ts';
import { useLegacyColors } from '../theme/useLegacyColors';
@@ -149,12 +147,6 @@ export const ThreadScreen: React.FC = ({
return (
-
= ({
shouldUseFlashList={messageListImplementation === 'flashlist'}
/>
-
);
};
diff --git a/package/src/__tests__/offline-support/offline-feature.js b/package/src/__tests__/offline-support/offline-feature.js
index bf6b72e38e..970338ee02 100644
--- a/package/src/__tests__/offline-support/offline-feature.js
+++ b/package/src/__tests__/offline-support/offline-feature.js
@@ -49,7 +49,7 @@ import { BetterSqlite } from '../../test-utils/BetterSqlite';
* to debug.
*/
/**
- * Custom Preview component used via WithComponents.
+ * Custom ChannelPreview component used via WithComponents.
* Receives { channel, muted, unread, lastMessage } from ChannelPreview.
*/
const ChannelPreviewComponent = ({ channel }) => (
@@ -207,7 +207,7 @@ export const Generic = () => {
const renderComponent = () =>
render(
-
+
,
diff --git a/package/src/components/Channel/Channel.tsx b/package/src/components/Channel/Channel.tsx
index 0cde399ac9..2a2893b67a 100644
--- a/package/src/components/Channel/Channel.tsx
+++ b/package/src/components/Channel/Channel.tsx
@@ -235,7 +235,6 @@ export type ChannelPropsWithContext = Pick &
MessagesContextValue,
| 'additionalPressableProps'
| 'customMessageSwipeAction'
- | 'deletedMessagesVisibilityType'
| 'disableTypingIndicator'
| 'dismissKeyboardOnMessageTouch'
| 'enableSwipeToReply'
@@ -405,7 +404,6 @@ const ChannelWithContext = (props: PropsWithChildren) =
compressImageQuality,
createPollOptionGap,
customMessageSwipeAction,
- deletedMessagesVisibilityType = 'always',
disableKeyboardCompatibleView = false,
disableTypingIndicator,
dismissKeyboardOnMessageTouch = true,
@@ -1635,7 +1633,6 @@ const ChannelWithContext = (props: PropsWithChildren) =
additionalPressableProps,
channelId,
customMessageSwipeAction,
- deletedMessagesVisibilityType,
deleteMessage,
deleteReaction,
disableTypingIndicator,
diff --git a/package/src/components/Channel/hooks/useCreateMessagesContext.ts b/package/src/components/Channel/hooks/useCreateMessagesContext.ts
index 62c375cec6..e90b13ef3b 100644
--- a/package/src/components/Channel/hooks/useCreateMessagesContext.ts
+++ b/package/src/components/Channel/hooks/useCreateMessagesContext.ts
@@ -6,7 +6,6 @@ export const useCreateMessagesContext = ({
additionalPressableProps,
channelId,
customMessageSwipeAction,
- deletedMessagesVisibilityType,
deleteMessage,
deleteReaction,
disableTypingIndicator,
@@ -70,7 +69,6 @@ export const useCreateMessagesContext = ({
() => ({
additionalPressableProps,
customMessageSwipeAction,
- deletedMessagesVisibilityType,
deleteMessage,
deleteReaction,
disableTypingIndicator,
diff --git a/package/src/components/ChannelList/ChannelListView.tsx b/package/src/components/ChannelList/ChannelListView.tsx
index 23814ae7a1..3731d7f746 100644
--- a/package/src/components/ChannelList/ChannelListView.tsx
+++ b/package/src/components/ChannelList/ChannelListView.tsx
@@ -26,7 +26,8 @@ const StatusIndicator = () => {
const { isOnline } = useChatContext();
const styles = useStyles();
const { error, loadingChannels, refreshList } = useChannelsContext();
- const { HeaderErrorIndicator, HeaderNetworkDownIndicator } = useComponentsContext();
+ const { ChannelListHeaderErrorIndicator, ChannelListHeaderNetworkDownIndicator } =
+ useComponentsContext();
if (loadingChannels) {
return null;
@@ -35,13 +36,13 @@ const StatusIndicator = () => {
if (!isOnline) {
return (
-
+
);
} else if (error) {
return (
-
+
);
}
@@ -72,7 +73,7 @@ const ChannelListViewWithContext = (props: ChannelListViewPropsWithContext) => {
} = props;
const {
EmptyStateIndicator,
- FooterLoadingIndicator,
+ ChannelListFooterLoadingIndicator,
ListHeaderComponent,
LoadingErrorIndicator,
ChannelListLoadingIndicator: LoadingIndicator,
@@ -138,7 +139,7 @@ const ChannelListViewWithContext = (props: ChannelListViewPropsWithContext) => {
ListEmptyComponent={
loading ? :
}
- ListFooterComponent={loadingNextPage ? : undefined}
+ ListFooterComponent={loadingNextPage ? : undefined}
ListHeaderComponent={ListHeaderComponent}
onEndReached={onEndReached}
onEndReachedThreshold={loadMoreThreshold}
diff --git a/package/src/components/ChannelList/__tests__/ChannelList.test.js b/package/src/components/ChannelList/__tests__/ChannelList.test.js
index 5700d93027..3fdadd4b15 100644
--- a/package/src/components/ChannelList/__tests__/ChannelList.test.js
+++ b/package/src/components/ChannelList/__tests__/ChannelList.test.js
@@ -46,7 +46,7 @@ jest.mock('../../ChannelPreview/ChannelSwipableWrapper', () => ({
}));
/**
- * Custom Preview component used via WithComponents to verify channel rendering.
+ * Custom ChannelPreview component used via WithComponents to verify channel rendering.
* Receives { channel, muted, unread, lastMessage } from ChannelPreview.
*/
const ChannelPreviewComponent = ({ channel }) => (
@@ -58,7 +58,7 @@ const ChannelPreviewComponent = ({ channel }) => (
/**
* Probe that reads swipeActionsEnabled from ChannelsContext.
- * Used as a Preview override to inspect context values.
+ * Used as a ChannelPreview override to inspect context values.
*/
const SwipeActionsProbe = () => {
const { swipeActionsEnabled } = useChannelsContext();
@@ -119,7 +119,7 @@ describe('ChannelList', () => {
const { getByTestId } = render(
-
+
,
@@ -133,7 +133,7 @@ describe('ChannelList', () => {
const { getByTestId } = render(
-
+
,
@@ -147,7 +147,7 @@ describe('ChannelList', () => {
render(
-
+
,
@@ -162,7 +162,7 @@ describe('ChannelList', () => {
screen.rerender(
-
+
,
@@ -204,7 +204,7 @@ describe('ChannelList', () => {
const { rerender, queryByTestId } = render(
-
+
,
@@ -224,7 +224,7 @@ describe('ChannelList', () => {
rerender(
-
+
,
@@ -254,7 +254,7 @@ describe('ChannelList', () => {
render(
-
+
,
@@ -276,7 +276,7 @@ describe('ChannelList', () => {
const { getByTestId } = render(
-
+
,
@@ -291,7 +291,7 @@ describe('ChannelList', () => {
const { getByTestId } = render(
-
+
,
@@ -306,7 +306,7 @@ describe('ChannelList', () => {
const { getByTestId, queryByTestId } = render(
-
+
,
@@ -323,7 +323,7 @@ describe('ChannelList', () => {
const { getByTestId } = render(
-
+
,
@@ -344,7 +344,7 @@ describe('ChannelList', () => {
@@ -366,7 +366,7 @@ describe('ChannelList', () => {
@@ -395,7 +395,7 @@ describe('ChannelList', () => {
it('should move channel to top of the list by default', async () => {
render(
-
+
,
@@ -419,7 +419,7 @@ describe('ChannelList', () => {
it('should add channel to top if channel is hidden from the list', async () => {
render(
-
+
,
@@ -449,7 +449,7 @@ describe('ChannelList', () => {
it('should not alter order if `lockChannelOrder` prop is true', async () => {
render(
-
+
,
@@ -475,7 +475,7 @@ describe('ChannelList', () => {
const onNewMessage = jest.fn();
render(
-
+
,
@@ -504,7 +504,7 @@ describe('ChannelList', () => {
it('should move a channel to top of the list by default', async () => {
render(
-
+
,
@@ -528,7 +528,7 @@ describe('ChannelList', () => {
const onNewMessage = jest.fn();
render(
-
+
,
@@ -549,7 +549,7 @@ describe('ChannelList', () => {
const onNewMessageNotification = jest.fn();
render(
-
+
,
@@ -578,7 +578,7 @@ describe('ChannelList', () => {
it('should move a channel to top of the list by default', async () => {
render(
-
+
,
@@ -605,7 +605,7 @@ describe('ChannelList', () => {
const onAddedToChannel = jest.fn();
render(
-
+
,
@@ -631,7 +631,7 @@ describe('ChannelList', () => {
it('should remove the channel from list by default', async () => {
render(
-
+
,
@@ -658,7 +658,7 @@ describe('ChannelList', () => {
const onRemovedFromChannel = jest.fn();
render(
-
+
,
@@ -684,7 +684,7 @@ describe('ChannelList', () => {
it('should update a channel in the list by default', async () => {
render(
-
+
,
@@ -710,7 +710,7 @@ describe('ChannelList', () => {
const onChannelUpdated = jest.fn();
render(
-
+
,
@@ -741,7 +741,7 @@ describe('ChannelList', () => {
it('should remove a channel from the list by default', async () => {
render(
-
+
,
@@ -768,7 +768,7 @@ describe('ChannelList', () => {
const onChannelDeleted = jest.fn();
render(
-
+
,
@@ -794,7 +794,7 @@ describe('ChannelList', () => {
it('should hide a channel from the list by default', async () => {
render(
-
+
,
@@ -821,7 +821,7 @@ describe('ChannelList', () => {
const onChannelHidden = jest.fn();
render(
-
+
,
@@ -846,7 +846,7 @@ describe('ChannelList', () => {
render(
-
+
,
@@ -874,7 +874,7 @@ describe('ChannelList', () => {
render(
-
+
,
@@ -909,7 +909,7 @@ describe('ChannelList', () => {
const onChannelTruncated = jest.fn();
render(
-
+
,
diff --git a/package/src/components/ChannelPreview/ChannelPreview.tsx b/package/src/components/ChannelPreview/ChannelPreview.tsx
index 81b386b95b..79c0839ffc 100644
--- a/package/src/components/ChannelPreview/ChannelPreview.tsx
+++ b/package/src/components/ChannelPreview/ChannelPreview.tsx
@@ -26,7 +26,7 @@ export const ChannelPreview = (props: ChannelPreviewProps) => {
const { client: contextClient } = useChatContext();
const { getChannelActionItems, swipeActionsEnabled } = useChannelsContext();
- const { Preview } = useComponentsContext();
+ const { ChannelPreview } = useComponentsContext();
const client = propClient || contextClient;
@@ -37,12 +37,12 @@ export const ChannelPreview = (props: ChannelPreviewProps) => {
const message = translatedLastMessage ? translatedLastMessage : lastMessage;
if (!swipeActionsEnabled) {
- return ;
+ return ;
}
return (
-
+
);
};
diff --git a/package/src/components/ChannelPreview/ChannelPreviewMessage.tsx b/package/src/components/ChannelPreview/ChannelPreviewMessage.tsx
index 2dbed3fd35..b086fe553e 100644
--- a/package/src/components/ChannelPreview/ChannelPreviewMessage.tsx
+++ b/package/src/components/ChannelPreview/ChannelPreviewMessage.tsx
@@ -26,8 +26,11 @@ export type ChannelPreviewMessageProps = Pick &
export const ChannelPreviewMessage = (props: ChannelPreviewMessageProps) => {
const { channel, lastMessage } = props;
- const { PreviewTypingIndicator, PreviewMessageDeliveryStatus, PreviewLastMessage } =
- useComponentsContext();
+ const {
+ ChannelPreviewTypingIndicator,
+ ChannelPreviewMessageDeliveryStatus,
+ ChannelPreviewLastMessage,
+ } = useComponentsContext();
const {
theme: { semantics },
} = useTheme();
@@ -53,14 +56,14 @@ export const ChannelPreviewMessage = (props: ChannelPreviewMessageProps) => {
const showMessageDeliveryStatus = !isMessageDeleted;
if (usersTyping.length > 0) {
- return ;
+ return ;
}
if (draftMessage) {
return (
{t('Draft')}:
-
+
);
}
@@ -98,18 +101,18 @@ export const ChannelPreviewMessage = (props: ChannelPreviewMessageProps) => {
return (
{showMessageDeliveryStatus ? (
-
+
) : null}
-
+
);
} else {
return (
{showMessageDeliveryStatus ? (
-
+
) : null}
-
+
);
}
diff --git a/package/src/components/ChannelPreview/ChannelPreviewView.tsx b/package/src/components/ChannelPreview/ChannelPreviewView.tsx
index 4bb0f224e8..f57c2e066b 100644
--- a/package/src/components/ChannelPreview/ChannelPreviewView.tsx
+++ b/package/src/components/ChannelPreview/ChannelPreviewView.tsx
@@ -46,12 +46,12 @@ const ChannelPreviewViewWithContext = (props: ChannelPreviewViewPropsWithContext
lastMessage,
} = props;
const {
- PreviewAvatar,
- PreviewMessage,
- PreviewMutedStatus,
- PreviewStatus,
- PreviewTitle,
- PreviewUnreadCount,
+ ChannelPreviewAvatar,
+ ChannelPreviewMessage,
+ ChannelPreviewMutedStatus,
+ ChannelPreviewStatus,
+ ChannelPreviewTitle,
+ ChannelPreviewUnreadCount,
} = useComponentsContext();
const {
@@ -97,24 +97,26 @@ const ChannelPreviewViewWithContext = (props: ChannelPreviewViewPropsWithContext
]}
testID='channel-preview-button'
>
-
+
-
- {muted && mutedStatusPosition === 'inlineTitle' ? : null}
+
+ {muted && mutedStatusPosition === 'inlineTitle' ? (
+
+ ) : null}
-
-
-
- {muted && mutedStatusPosition === 'trailingBottom' ? : null}
+
+ {muted && mutedStatusPosition === 'trailingBottom' ? (
+
+ ) : null}
diff --git a/package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx b/package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx
index 42cb1ed3e2..fbf44b83ac 100644
--- a/package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx
+++ b/package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx
@@ -84,7 +84,7 @@ describe('ChannelPreview', () => {
return (
-
+
@@ -437,7 +437,7 @@ describe('ChannelPreview', () => {
{
});
});
- it('should render deleted message in the list when `deleteMessagesVisibilityType` is set to default(always)', async () => {
+ it('should render deleted message in the list', async () => {
const user1 = generateUser();
const mockedChannel = generateChannelResponse({
members: [generateMember({ user: user1 })],
@@ -124,135 +124,6 @@ describe('MessageList', () => {
});
});
- it('should render deleted message in the list when `deleteMessagesVisibilityType` is set to sender', async () => {
- const user1 = generateUser();
- const user2 = generateUser({ id: 'testID' });
- const mockedChannel = generateChannelResponse({
- members: [generateMember({ user: user1 })],
- messages: [
- generateMessage({ type: 'deleted', user: user2 }),
- generateMessage({ type: 'system', user: undefined }),
- generateMessage({ user: user1 }),
- ],
- });
-
- const chatClient = await getTestClientWithUser({ id: 'testID' });
- useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
- const channel = chatClient.channel('messaging', mockedChannel.id);
- await channel.watch();
-
- const { queryByTestId } = render(
-
-
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(queryByTestId('message-deleted')).toBeTruthy();
- });
- });
-
- it('should render deleted message in the list when `deleteMessagesVisibilityType` is set to receiver', async () => {
- const user1 = generateUser();
- const user2 = generateUser({ id: 'testID' });
- const mockedChannel = generateChannelResponse({
- members: [generateMember({ user: user1 })],
- messages: [
- generateMessage({ user: user2 }),
- generateMessage({ type: 'system', user: undefined }),
- generateMessage({ type: 'deleted', user: user1 }),
- ],
- });
-
- const chatClient = await getTestClientWithUser({ id: 'testID' });
- useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
- const channel = chatClient.channel('messaging', mockedChannel.id);
- await channel.watch();
-
- const { getByTestId, queryByTestId } = render(
-
-
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(getByTestId('message-deleted')).toBeTruthy();
- expect(queryByTestId('only-visible-to-you')).toBeNull();
- });
- });
-
- it('should render deleted message in the list when `deleteMessagesVisibilityType` is set to never', async () => {
- const user1 = generateUser();
- const user2 = generateUser({ id: 'testID' });
- const mockedChannel = generateChannelResponse({
- members: [generateMember({ user: user1 })],
- messages: [
- generateMessage({ user: user2 }),
- generateMessage({ type: 'system', user: undefined }),
- generateMessage({ type: 'deleted', user: user1 }),
- ],
- });
-
- const chatClient = await getTestClientWithUser({ id: 'testID' });
- useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
- const channel = chatClient.channel('messaging', mockedChannel.id);
- await channel.watch();
-
- const { queryByTestId } = render(
-
-
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(queryByTestId('message-deleted')).toBeNull();
- expect(queryByTestId('only-visible-to-you')).toBeNull();
- });
- });
-
- it('should render deleted message in the list', async () => {
- const user1 = generateUser();
- const mockedChannel = generateChannelResponse({
- members: [generateMember({ user: user1 })],
- messages: [
- generateMessage({ type: 'deleted', user: user1 }),
- generateMessage({ type: 'system', user: undefined }),
- generateMessage({ user: user1 }),
- ],
- });
-
- const chatClient = await getTestClientWithUser({ id: 'testID' });
- useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
- const channel = chatClient.channel('messaging', mockedChannel.id);
- await channel.watch();
-
- const { getByTestId } = render(
-
-
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(getByTestId('message-deleted')).toBeTruthy();
- });
- });
-
it('should render the typing indicator when typing object is non empty', async () => {
const user1 = generateUser();
const mockedChannel = generateChannelResponse({
diff --git a/package/src/components/MessageList/hooks/useMessageList.ts b/package/src/components/MessageList/hooks/useMessageList.ts
index 61d129238c..73dac61e24 100644
--- a/package/src/components/MessageList/hooks/useMessageList.ts
+++ b/package/src/components/MessageList/hooks/useMessageList.ts
@@ -2,11 +2,6 @@ import { useMemo } from 'react';
import type { LocalMessage } from 'stream-chat';
-import { useChatContext } from '../../../contexts/chatContext/ChatContext';
-import {
- DeletedMessagesVisibilityType,
- useMessagesContext,
-} from '../../../contexts/messagesContext/MessagesContext';
import { usePaginatedMessageListContext } from '../../../contexts/paginatedMessageListContext/PaginatedMessageListContext';
import { useThreadContext } from '../../../contexts/threadContext/ThreadContext';
@@ -27,35 +22,8 @@ export type MessageGroupStyles = {
[key: string]: string[];
};
-export const shouldIncludeMessageInList = (
- message: LocalMessage,
- options: { deletedMessagesVisibilityType?: DeletedMessagesVisibilityType; userId?: string },
-) => {
- const { deletedMessagesVisibilityType, userId } = options;
- const isMessageTypeDeleted = message.type === 'deleted';
- const isSender = message.user?.id === userId;
-
- if (!isMessageTypeDeleted) {
- return true;
- }
-
- switch (deletedMessagesVisibilityType) {
- case 'always':
- return true;
- case 'sender':
- return isSender;
- case 'receiver':
- return !isSender;
- case 'never':
- default:
- return false;
- }
-};
-
export const useMessageList = (params: UseMessageListParams) => {
const { threadList, isLiveStreaming, isFlashList = false } = params;
- const { client } = useChatContext();
- const { deletedMessagesVisibilityType } = useMessagesContext();
const { messages, viewabilityChangedCallback } = usePaginatedMessageListContext();
const { threadMessages } = useThreadContext();
const messageList = threadList ? threadMessages : messages;
@@ -63,14 +31,6 @@ export const useMessageList = (params: UseMessageListParams) => {
const processedMessageList = useMemo(() => {
const newMessageList = [];
for (const message of messageList) {
- if (
- !shouldIncludeMessageInList(message, {
- deletedMessagesVisibilityType,
- userId: client.userID,
- })
- ) {
- continue;
- }
if (isFlashList) {
newMessageList.push(message);
} else {
@@ -78,7 +38,7 @@ export const useMessageList = (params: UseMessageListParams) => {
}
}
return newMessageList;
- }, [messageList, deletedMessagesVisibilityType, client.userID, isFlashList]);
+ }, [messageList, isFlashList]);
const data = useRAFCoalescedValue(processedMessageList, isLiveStreaming);
diff --git a/package/src/contexts/channelsContext/ChannelsContext.tsx b/package/src/contexts/channelsContext/ChannelsContext.tsx
index b375a7997c..0dd3b2738e 100644
--- a/package/src/contexts/channelsContext/ChannelsContext.tsx
+++ b/package/src/contexts/channelsContext/ChannelsContext.tsx
@@ -51,7 +51,8 @@ export type ChannelsContextValue = {
*/
loadingChannels: boolean;
/**
- * Whether or not additional channels are being loaded, triggers the FooterLoadingIndicator
+ * Whether or not additional channels are being loaded, triggers the
+ * ChannelListFooterLoadingIndicator
*/
loadingNextPage: boolean;
/**
diff --git a/package/src/contexts/componentsContext/PLAN.md b/package/src/contexts/componentsContext/PLAN.md
index d19280d2b5..bee0d5e58c 100644
--- a/package/src/contexts/componentsContext/PLAN.md
+++ b/package/src/contexts/componentsContext/PLAN.md
@@ -69,26 +69,26 @@ const getDefaults = () => {
Some component keys differ from their default component names to avoid collisions:
-| Override Key | Default Component | Why renamed |
-| ----------------------------- | --------------------------------------- | ---------------------------------------------------------- |
-| `FileAttachmentIcon` | `FileIcon` | Clarity |
-| `ChannelListLoadingIndicator` | `ChannelListLoadingIndicator` | Split from shared `LoadingIndicator` — renders skeleton UI |
-| `MessageListLoadingIndicator` | `LoadingIndicator` | Split from shared `LoadingIndicator` — renders text |
-| `ChatLoadingIndicator` | `undefined` | Optional, no default |
-| `ThreadMessageComposer` | `MessageComposer` | Avoid collision with `MessageComposer` component name |
-| `ThreadListComponent` | `DefaultThreadListComponent` | Avoid collision with exported `ThreadList` |
-| `StartAudioRecordingButton` | `AudioRecordingButton` | Historical naming |
-| `Preview` | `ChannelPreviewView` | ChannelList preview item |
-| `PreviewAvatar` | `ChannelAvatar` | ChannelList preview avatar |
-| `FooterLoadingIndicator` | `ChannelListFooterLoadingIndicator` | ChannelList footer |
-| `HeaderErrorIndicator` | `ChannelListHeaderErrorIndicator` | ChannelList header |
-| `HeaderNetworkDownIndicator` | `ChannelListHeaderNetworkDownIndicator` | ChannelList header |
+| Override Key | Default Component | Why renamed |
+| --------------------------------------- | --------------------------------------- | ---------------------------------------------------------- |
+| `FileAttachmentIcon` | `FileIcon` | Clarity |
+| `ChannelListLoadingIndicator` | `ChannelListLoadingIndicator` | Split from shared `LoadingIndicator` — renders skeleton UI |
+| `MessageListLoadingIndicator` | `LoadingIndicator` | Split from shared `LoadingIndicator` — renders text |
+| `ChatLoadingIndicator` | `undefined` | Optional, no default |
+| `ThreadMessageComposer` | `MessageComposer` | Avoid collision with `MessageComposer` component name |
+| `ThreadListComponent` | `DefaultThreadListComponent` | Avoid collision with exported `ThreadList` |
+| `StartAudioRecordingButton` | `AudioRecordingButton` | Historical naming |
+| `ChannelPreview` | `ChannelPreviewView` | ChannelList preview item |
+| `ChannelPreviewAvatar` | `ChannelAvatar` | ChannelList preview avatar |
+| `ChannelListFooterLoadingIndicator` | `ChannelListFooterLoadingIndicator` | ChannelList footer |
+| `ChannelListHeaderErrorIndicator` | `ChannelListHeaderErrorIndicator` | ChannelList header |
+| `ChannelListHeaderNetworkDownIndicator` | `ChannelListHeaderNetworkDownIndicator` | ChannelList header |
### Optional Components (no default)
These exist in `DEFAULT_COMPONENTS` as `undefined` with `React.ComponentType | undefined` type assertions:
-`AttachmentPickerIOSSelectMorePhotos`, `ChatLoadingIndicator`, `CreatePollContent`, `ImageComponent`, `Input`, `ListHeaderComponent`, `MessageContentBottomView`, `MessageContentLeadingView`, `MessageContentTopView`, `MessageContentTrailingView`, `MessageLocation`, `MessageSpacer`, `MessageText`, `PollContent`
+`AttachmentPickerIOSSelectMorePhotos`, `ChatLoadingIndicator`, `CreatePollContent`, `ImageComponent`, `Input`, `ListHeaderComponent`, `MessageActions`, `MessageContentBottomView`, `MessageContentLeadingView`, `MessageContentTopView`, `MessageContentTrailingView`, `MessageLocation`, `MessageSpacer`, `MessageText`, `PollContent`
### Shared Component Keys (audited)
diff --git a/package/src/contexts/componentsContext/__tests__/defaultComponents.test.ts b/package/src/contexts/componentsContext/__tests__/defaultComponents.test.ts
index 4742193501..10a5326532 100644
--- a/package/src/contexts/componentsContext/__tests__/defaultComponents.test.ts
+++ b/package/src/contexts/componentsContext/__tests__/defaultComponents.test.ts
@@ -5,6 +5,7 @@ const OPTIONAL_KEYS = new Set([
'AttachmentPickerIOSSelectMorePhotos',
'ChatLoadingIndicator',
'CreatePollContent',
+ 'MessageActions',
'Input',
'ListHeaderComponent',
'MessageContentBottomView',
diff --git a/package/src/contexts/componentsContext/defaultComponents.ts b/package/src/contexts/componentsContext/defaultComponents.ts
index 12af4049ea..bfa5abc961 100644
--- a/package/src/contexts/componentsContext/defaultComponents.ts
+++ b/package/src/contexts/componentsContext/defaultComponents.ts
@@ -144,6 +144,7 @@ import { ThreadListUnreadBanner } from '../../components/ThreadList/ThreadListUn
import { ThreadMessagePreviewDeliveryStatus } from '../../components/ThreadList/ThreadMessagePreviewDeliveryStatus';
import { ChannelAvatar } from '../../components/ui/Avatar/ChannelAvatar';
import { DefaultMessageOverlayBackground } from '../../contexts/overlayContext/MessageOverlayHostLayer';
+import type { MessageActionsProps } from '../../contexts/overlayContext/MessageOverlayHostLayer';
/**
* All default component implementations used across the SDK.
@@ -177,11 +178,11 @@ export const DEFAULT_COMPONENTS = {
FileUploadNotSupportedIndicator,
FileUploadRetryIndicator,
FilePreview,
- FooterLoadingIndicator: ChannelListFooterLoadingIndicator,
+ ChannelListFooterLoadingIndicator,
Gallery,
Giphy,
- HeaderErrorIndicator: ChannelListHeaderErrorIndicator,
- HeaderNetworkDownIndicator: ChannelListHeaderNetworkDownIndicator,
+ ChannelListHeaderErrorIndicator,
+ ChannelListHeaderNetworkDownIndicator,
ImageAttachmentUploadPreview,
ImageLoadingFailedIndicator,
ImageLoadingIndicator,
@@ -231,16 +232,16 @@ export const DEFAULT_COMPONENTS = {
MessageUserReactionsAvatar,
MessageUserReactionsItem,
NetworkDownIndicator,
- Preview: ChannelPreviewView,
- PreviewAvatar: ChannelAvatar,
- PreviewLastMessage: ChannelLastMessagePreview,
- PreviewMessage: ChannelPreviewMessage,
- PreviewMessageDeliveryStatus: ChannelMessagePreviewDeliveryStatus,
- PreviewMutedStatus: ChannelPreviewMutedStatus,
- PreviewStatus: ChannelPreviewStatus,
- PreviewTitle: ChannelPreviewTitle,
- PreviewTypingIndicator: ChannelPreviewTypingIndicator,
- PreviewUnreadCount: ChannelPreviewUnreadCount,
+ ChannelPreview: ChannelPreviewView,
+ ChannelPreviewAvatar: ChannelAvatar,
+ ChannelPreviewLastMessage: ChannelLastMessagePreview,
+ ChannelPreviewMessage,
+ ChannelPreviewMessageDeliveryStatus: ChannelMessagePreviewDeliveryStatus,
+ ChannelPreviewMutedStatus,
+ ChannelPreviewStatus,
+ ChannelPreviewTitle,
+ ChannelPreviewTypingIndicator,
+ ChannelPreviewUnreadCount,
ReactionListBottom,
ReactionListClustered,
ReactionListCountItem,
@@ -309,6 +310,7 @@ export const DEFAULT_COMPONENTS = {
ChatLoadingIndicator: undefined as React.ComponentType | null | undefined,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
CreatePollContent: undefined as React.ComponentType | undefined,
+ MessageActions: undefined as React.ComponentType | undefined,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Input: undefined as React.ComponentType | undefined,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
diff --git a/package/src/contexts/messagesContext/MessagesContext.tsx b/package/src/contexts/messagesContext/MessagesContext.tsx
index 210ed7e24e..badfc58d94 100644
--- a/package/src/contexts/messagesContext/MessagesContext.tsx
+++ b/package/src/contexts/messagesContext/MessagesContext.tsx
@@ -39,7 +39,6 @@ export type MessageContentType =
| 'ai_text'
| 'text'
| 'location';
-export type DeletedMessagesVisibilityType = 'always' | 'never' | 'receiver' | 'sender';
export type MessageLocationProps = {
message: LocalMessage;
@@ -112,14 +111,6 @@ export type MessagesContextValue = Pick void;
- /**
- * Full override of the delete message button in the Message Actions
- *
- * Please check [cookbook](https://github.com/GetStream/stream-chat-react-native/wiki/Cookbook-v3.0#override-or-intercept-message-actions-edit-delete-reaction-reply-etc) for details.
- */
- /** Control if the deleted message is visible to both the send and reciever, either of them or none */
- deletedMessagesVisibilityType?: DeletedMessagesVisibilityType;
-
disableTypingIndicator?: boolean;
/**
* Enable swipe to reply on messages.
diff --git a/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx b/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx
index e4844914c1..520a0b55e9 100644
--- a/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx
+++ b/package/src/contexts/overlayContext/MessageOverlayHostLayer.tsx
@@ -4,8 +4,10 @@ import {
Platform,
Pressable,
StyleSheet,
+ StyleProp,
useWindowDimensions,
View,
+ ViewStyle,
} from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
@@ -53,8 +55,15 @@ export const DefaultMessageOverlayBackground = () => {
);
};
+export type MessageActionsProps = {
+ bottomItemStyle: StyleProp;
+ hostStyle: StyleProp;
+ portalHostStyle: StyleProp;
+ topItemStyle: StyleProp;
+};
+
export const MessageOverlayHostLayer = () => {
- const { MessageOverlayBackground } = useComponentsContext();
+ const { MessageActions, MessageOverlayBackground } = useComponentsContext();
const { id, closing } = useOverlayController();
const insets = useSafeAreaInsets();
const { height: screenH } = useWindowDimensions();
@@ -259,21 +268,32 @@ export const MessageOverlayHostLayer = () => {
/>
) : null}
-
-
-
-
-
-
-
-
-
-
-
+ {MessageActions ? (
+
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
diff --git a/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx b/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx
index 7facffe86d..bf826461d9 100644
--- a/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx
+++ b/package/src/contexts/overlayContext/__tests__/MessageOverlayHostLayer.test.tsx
@@ -14,7 +14,7 @@ import {
setOverlayTopH,
} from '../../../state-store';
import { WithComponents } from '../../componentsContext/ComponentsContext';
-import { MessageOverlayHostLayer } from '../MessageOverlayHostLayer';
+import { MessageActionsProps, MessageOverlayHostLayer } from '../MessageOverlayHostLayer';
jest.mock('react-native', () => {
const actual = jest.requireActual('react-native');
@@ -93,6 +93,18 @@ const TOP_RECT = { h: 20, w: 90, x: 5, y: 0 };
const MESSAGE_RECT = { h: 50, w: 180, x: 10, y: 0 };
const BOTTOM_RECT = { h: 30, w: 140, x: 20, y: 100 };
const NoopBackground = () => null;
+const CustomMessageActions = ({
+ bottomItemStyle,
+ hostStyle,
+ topItemStyle,
+}: MessageActionsProps) => (
+ <>
+ Custom
+
+
+
+ >
+);
const flushAnimationFrameQueue = () => {
act(() => {
@@ -260,4 +272,50 @@ describe('MessageOverlayHostLayer', () => {
height: 0,
});
});
+
+ it('renders MessageActions override instead of the default host wrappers when provided', () => {
+ const renderTree = () => (
+
+
+
+ );
+ const { rerender } = render(renderTree());
+
+ act(() => {
+ setOverlayTopH(TOP_RECT);
+ setOverlayMessageH(MESSAGE_RECT);
+ setOverlayBottomH(BOTTOM_RECT);
+ openOverlay('message-1');
+ });
+
+ rerender(renderTree());
+
+ expect(screen.getByTestId('custom-message-actions')).toBeTruthy();
+ expect(screen.queryByTestId('message-overlay-top')).toBeNull();
+ expect(screen.queryByTestId('message-overlay-message')).toBeNull();
+ expect(screen.queryByTestId('message-overlay-bottom')).toBeNull();
+ expect(
+ StyleSheet.flatten(screen.getByTestId('custom-message-actions-top').props.style),
+ ).toMatchObject({
+ height: TOP_RECT.h,
+ width: TOP_RECT.w,
+ });
+ expect(
+ StyleSheet.flatten(screen.getByTestId('custom-message-actions-message').props.style),
+ ).toMatchObject({
+ height: MESSAGE_RECT.h,
+ width: MESSAGE_RECT.w,
+ });
+ expect(
+ StyleSheet.flatten(screen.getByTestId('custom-message-actions-bottom').props.style),
+ ).toMatchObject({
+ height: BOTTOM_RECT.h,
+ width: BOTTOM_RECT.w,
+ });
+ });
});