Skip to content

Commit b0a5125

Browse files
authored
feat: improve reaction list in message customization (#3473)
This pull request enhances the customization and flexibility of the reaction list UI components in the chat application. It introduces new context properties and props for various reaction list subcomponents, allowing developers to override and customize individual parts of the reaction list (such as clustered views, items, wrappers, and count items) both globally and at the component level. This enables more granular control over the appearance and behavior of message reactions. Key changes include: **Customization API for Reaction List Components:** - Added new context properties to `MessagesContextValue` for `ReactionListClustered`, `ReactionListItem`, `ReactionListItemWrapper`, and `ReactionListCountItem`, allowing these subcomponents to be overridden via context. - Updated the `Channel` component and its context to accept and propagate the new reaction list component overrides, making them available throughout the component tree. [[1]](diffhunk://#diff-f7139f4cdb523365cfc277d72b827a3432325b9c6460cf14628f9df67d0e4d85R161-R164) [[2]](diffhunk://#diff-f7139f4cdb523365cfc277d72b827a3432325b9c6460cf14628f9df67d0e4d85R400-R403) [[3]](diffhunk://#diff-f7139f4cdb523365cfc277d72b827a3432325b9c6460cf14628f9df67d0e4d85R733-R736) [[4]](diffhunk://#diff-f7139f4cdb523365cfc277d72b827a3432325b9c6460cf14628f9df67d0e4d85R1993-R1996) [[5]](diffhunk://#diff-d3e4f4cdcef10807a38eab86f6ead6bbfe01980e7326f3cadfeb8186760d7af1R94-R97) [[6]](diffhunk://#diff-d3e4f4cdcef10807a38eab86f6ead6bbfe01980e7326f3cadfeb8186760d7af1R214-R217) **Component Propagation and Usage:** - Modified `ReactionListTop` and `ReactionListBottom` to accept and prioritize the new override props, falling back to context values if not provided, and updated their rendering logic to use these customizable components. [[1]](diffhunk://#diff-1d5290d5eb1b5942da3dd1386029e3951eb7a08188398c0d943bd16d378da24aL32-R37) [[2]](diffhunk://#diff-1d5290d5eb1b5942da3dd1386029e3951eb7a08188398c0d943bd16d378da24aR59-R61) [[3]](diffhunk://#diff-1d5290d5eb1b5942da3dd1386029e3951eb7a08188398c0d943bd16d378da24aL70-R81) [[4]](diffhunk://#diff-1d5290d5eb1b5942da3dd1386029e3951eb7a08188398c0d943bd16d378da24aR93-R96) [[5]](diffhunk://#diff-1d5290d5eb1b5942da3dd1386029e3951eb7a08188398c0d943bd16d378da24aL101-R116) [[6]](diffhunk://#diff-1d5290d5eb1b5942da3dd1386029e3951eb7a08188398c0d943bd16d378da24aL115-R133) [[7]](diffhunk://#diff-926ded9f8a41b99e3b915a72424b44c11631585a26cfc0fa236d68979dafddb6L49-R34) [[8]](diffhunk://#diff-926ded9f8a41b99e3b915a72424b44c11631585a26cfc0fa236d68979dafddb6R58-R59) [[9]](diffhunk://#diff-926ded9f8a41b99e3b915a72424b44c11631585a26cfc0fa236d68979dafddb6L87-R78) [[10]](diffhunk://#diff-926ded9f8a41b99e3b915a72424b44c11631585a26cfc0fa236d68979dafddb6R90-R110) **Type and Export Updates:** - Updated type definitions and exports to ensure the new reaction list component types and props are available for use in other parts of the codebase and by consumers of the library. [[1]](diffhunk://#diff-784bafff5d1104bab8f993f0eab47d22373071f35a518685093bf641289b4e38L7-R7) [[2]](diffhunk://#diff-667ac1eb12bc400d53fad690273567753408afefc69a49fa9869641181e86dd8R109-R111) [[3]](diffhunk://#diff-327d7452e29943b63019b2b135e4c012a8fcd51a85fa38ec099c55db0d9d2cfeR52-R57) **Refactoring and Cleanup:** - Refactored the rendering logic in `ReactionListBottom` and `ReactionListTop` to use the customizable components, and removed hardcoded imports and renderers in favor of dynamic, context-driven rendering. [[1]](diffhunk://#diff-926ded9f8a41b99e3b915a72424b44c11631585a26cfc0fa236d68979dafddb6L1-R4) [[2]](diffhunk://#diff-926ded9f8a41b99e3b915a72424b44c11631585a26cfc0fa236d68979dafddb6L19-L34) [[3]](diffhunk://#diff-1d5290d5eb1b5942da3dd1386029e3951eb7a08188398c0d943bd16d378da24aL4-L7) These changes collectively provide a more modular and extensible approach to rendering reaction lists, making it easier for developers to implement custom designs or behaviors for message reactions.
1 parent 06b9eed commit b0a5125

File tree

7 files changed

+112
-33
lines changed

7 files changed

+112
-33
lines changed

package/src/components/Channel/Channel.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ import { MessageStatus as MessageStatusDefault } from '../Message/MessageSimple/
158158
import { MessageSwipeContent as MessageSwipeContentDefault } from '../Message/MessageSimple/MessageSwipeContent';
159159
import { MessageTimestamp as MessageTimestampDefault } from '../Message/MessageSimple/MessageTimestamp';
160160
import { ReactionListBottom as ReactionListBottomDefault } from '../Message/MessageSimple/ReactionList/ReactionListBottom';
161+
import { ReactionListClustered as ReactionListClusteredDefault } from '../Message/MessageSimple/ReactionList/ReactionListClustered';
162+
import { ReactionListCountItem as ReactionListCountItemDefault } from '../Message/MessageSimple/ReactionList/ReactionListItem';
163+
import { ReactionListItem as ReactionListItemDefault } from '../Message/MessageSimple/ReactionList/ReactionListItem';
164+
import { ReactionListItemWrapper as ReactionListItemWrapperDefault } from '../Message/MessageSimple/ReactionList/ReactionListItemWrapper';
161165
import { ReactionListTop as ReactionListTopDefault } from '../Message/MessageSimple/ReactionList/ReactionListTop';
162166
import { StreamingMessageView as DefaultStreamingMessageView } from '../Message/MessageSimple/StreamingMessageView';
163167
import { AttachmentUploadPreviewList as AttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList';
@@ -393,6 +397,10 @@ export type ChannelPropsWithContext = Pick<ChannelContextValue, 'channel'> &
393397
| 'reactionListPosition'
394398
| 'reactionListType'
395399
| 'ReactionListTop'
400+
| 'ReactionListClustered'
401+
| 'ReactionListItem'
402+
| 'ReactionListItemWrapper'
403+
| 'ReactionListCountItem'
396404
| 'Reply'
397405
| 'shouldShowUnreadUnderlay'
398406
| 'ScrollToBottomButton'
@@ -722,6 +730,10 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
722730
reactionListPosition = 'top',
723731
reactionListType = 'clustered',
724732
ReactionListTop = ReactionListTopDefault,
733+
ReactionListClustered = ReactionListClusteredDefault,
734+
ReactionListItem = ReactionListItemDefault,
735+
ReactionListItemWrapper = ReactionListItemWrapperDefault,
736+
ReactionListCountItem = ReactionListCountItemDefault,
725737
Reply = ReplyDefault,
726738
ScrollToBottomButton = ScrollToBottomButtonDefault,
727739
selectReaction,
@@ -1978,6 +1990,10 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
19781990
reactionListPosition,
19791991
reactionListType,
19801992
ReactionListTop,
1993+
ReactionListClustered,
1994+
ReactionListItem,
1995+
ReactionListItemWrapper,
1996+
ReactionListCountItem,
19811997
removeMessage,
19821998
Reply,
19831999
retrySendMessage,

package/src/components/Channel/hooks/useCreateMessagesContext.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ export const useCreateMessagesContext = ({
9191
reactionListPosition,
9292
reactionListType,
9393
ReactionListTop,
94+
ReactionListClustered,
95+
ReactionListItem,
96+
ReactionListItemWrapper,
97+
ReactionListCountItem,
9498
removeMessage,
9599
Reply,
96100
retrySendMessage,
@@ -207,6 +211,10 @@ export const useCreateMessagesContext = ({
207211
reactionListPosition,
208212
reactionListType,
209213
ReactionListTop,
214+
ReactionListClustered,
215+
ReactionListItem,
216+
ReactionListItemWrapper,
217+
ReactionListCountItem,
210218
removeMessage,
211219
Reply,
212220
retrySendMessage,

package/src/components/Message/MessageSimple/ReactionList/ReactionListBottom.tsx

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import React, { useMemo } from 'react';
1+
import React, { useCallback, useMemo } from 'react';
22
import { FlatList, StyleSheet, View } from 'react-native';
33

4-
import { ReactionListClustered } from './ReactionListClustered';
5-
import { ReactionListItem, ReactionListItemProps } from './ReactionListItem';
4+
import { ReactionListItemProps } from './ReactionListItem';
65

76
import {
87
MessageContextValue,
@@ -16,22 +15,6 @@ import { useTheme } from '../../../../contexts/themeContext/ThemeContext';
1615

1716
import { primitives } from '../../../../theme';
1817

19-
const renderItem = ({ index, item }: { index: number; item: ReactionListItemProps }) => (
20-
<ReactionListItem
21-
handleReaction={item.handleReaction}
22-
key={index}
23-
onLongPress={item.onLongPress}
24-
onPress={item.onPress}
25-
onPressIn={item.onPressIn}
26-
preventPress={item.preventPress}
27-
reaction={item.reaction}
28-
showReactionsOverlay={item.showReactionsOverlay}
29-
supportedReactions={item.supportedReactions}
30-
selected={item.reaction.own}
31-
showCount={item.showCount}
32-
/>
33-
);
34-
3518
export type ReactionListBottomProps = Partial<
3619
Pick<
3720
MessageContextValue,
@@ -46,7 +29,9 @@ export type ReactionListBottomProps = Partial<
4629
| 'showReactionsOverlay'
4730
>
4831
> &
49-
Partial<Pick<MessagesContextValue, 'supportedReactions'>> & {
32+
Partial<
33+
Pick<MessagesContextValue, 'supportedReactions' | 'ReactionListClustered' | 'ReactionListItem'>
34+
> & {
5035
type?: 'clustered' | 'segmented';
5136
showCount?: boolean;
5237
};
@@ -70,6 +55,8 @@ export const ReactionListBottom = (props: ReactionListBottomProps) => {
7055
supportedReactions: propSupportedReactions,
7156
type,
7257
showCount = true,
58+
ReactionListClustered: propReactionListClustered,
59+
ReactionListItem: propReactionListItem,
7360
} = props;
7461

7562
const {
@@ -84,7 +71,11 @@ export const ReactionListBottom = (props: ReactionListBottomProps) => {
8471
showReactionsOverlay: contextShowReactionsOverlay,
8572
} = useMessageContext();
8673

87-
const { supportedReactions: contextSupportedReactions } = useMessagesContext();
74+
const {
75+
supportedReactions: contextSupportedReactions,
76+
ReactionListClustered: contextReactionListClustered,
77+
ReactionListItem: contextReactionListItem,
78+
} = useMessagesContext();
8879

8980
const alignment = propAlignment || contextAlignment;
9081
const handleReaction = propHandlerReaction || contextHandleReaction;
@@ -96,6 +87,27 @@ export const ReactionListBottom = (props: ReactionListBottomProps) => {
9687
const reactions = propReactions || contextReactions;
9788
const showReactionsOverlay = propShowReactionsOverlay || contextShowReactionsOverlay;
9889
const supportedReactions = propSupportedReactions || contextSupportedReactions;
90+
const ReactionListClustered = propReactionListClustered || contextReactionListClustered;
91+
const ReactionListItem = propReactionListItem || contextReactionListItem;
92+
93+
const renderItem = useCallback(
94+
({ index, item }: { index: number; item: ReactionListItemProps }) => (
95+
<ReactionListItem
96+
handleReaction={item.handleReaction}
97+
key={index}
98+
onLongPress={item.onLongPress}
99+
onPress={item.onPress}
100+
onPressIn={item.onPressIn}
101+
preventPress={item.preventPress}
102+
reaction={item.reaction}
103+
showReactionsOverlay={item.showReactionsOverlay}
104+
supportedReactions={item.supportedReactions}
105+
selected={item.reaction.own}
106+
showCount={item.showCount}
107+
/>
108+
),
109+
[ReactionListItem],
110+
);
99111

100112
const styles = useStyles({ messageAlignment: alignment });
101113
const supportedReactionTypes = supportedReactions?.map(

package/src/components/Message/MessageSimple/ReactionList/ReactionListItemWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Pressable, PressableProps, StyleProp, StyleSheet, View, ViewStyle } fro
44
import { useTheme } from '../../../../contexts/themeContext/ThemeContext';
55
import { primitives } from '../../../../theme';
66

7-
type ReactionListItemWrapperProps = PressableProps & {
7+
export type ReactionListItemWrapperProps = PressableProps & {
88
selected?: boolean;
99
style?: StyleProp<ViewStyle>;
1010
};

package/src/components/Message/MessageSimple/ReactionList/ReactionListTop.tsx

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import React, { useMemo } from 'react';
22
import { ScrollView, StyleSheet, View } from 'react-native';
33

4-
import { ReactionListClustered } from './ReactionListClustered';
5-
6-
import { ReactionListCountItem, ReactionListItem } from './ReactionListItem';
7-
84
import { useTheme } from '../../../../contexts';
95
import {
106
MessageContextValue,
@@ -29,12 +25,19 @@ export type ReactionListTopProps = Partial<
2925
| 'reactions'
3026
| 'showReactionsOverlay'
3127
| 'handleReaction'
32-
>
33-
> &
34-
Pick<MessagesContextValue, 'supportedReactions' | 'reactionListType'> & {
35-
type?: 'clustered' | 'segmented';
36-
showCount?: boolean;
37-
};
28+
> &
29+
Pick<
30+
MessagesContextValue,
31+
| 'supportedReactions'
32+
| 'reactionListType'
33+
| 'ReactionListClustered'
34+
| 'ReactionListItem'
35+
| 'ReactionListCountItem'
36+
>
37+
> & {
38+
type?: 'clustered' | 'segmented';
39+
showCount?: boolean;
40+
};
3841

3942
/**
4043
* ReactionListTop - A high level component which implements all the logic required for a message reaction list
@@ -53,6 +56,9 @@ export const ReactionListTop = (props: ReactionListTopProps) => {
5356
handleReaction: propHandleReaction,
5457
type,
5558
showCount = true,
59+
ReactionListClustered: propReactionListClustered,
60+
ReactionListItem: propReactionListItem,
61+
ReactionListCountItem: propReactionListCountItem,
5662
} = props;
5763

5864
const {
@@ -67,7 +73,12 @@ export const ReactionListTop = (props: ReactionListTopProps) => {
6773
handleReaction: contextHandleReaction,
6874
} = useMessageContext();
6975

70-
const { supportedReactions: contextSupportedReactions } = useMessagesContext();
76+
const {
77+
supportedReactions: contextSupportedReactions,
78+
ReactionListClustered: contextReactionListClustered,
79+
ReactionListItem: contextReactionListItem,
80+
ReactionListCountItem: contextReactionListCountItem,
81+
} = useMessagesContext();
7182

7283
const alignment = propAlignment || contextAlignment;
7384
const hasReactions = propHasReactions || contextHasReactions;
@@ -79,6 +90,10 @@ export const ReactionListTop = (props: ReactionListTopProps) => {
7990
const showReactionsOverlay = propShowReactionsOverlay || contextShowReactionsOverlay;
8091
const supportedReactions = propSupportedReactions || contextSupportedReactions;
8192
const handleReaction = propHandleReaction || contextHandleReaction;
93+
const ReactionListClustered = propReactionListClustered || contextReactionListClustered;
94+
const ReactionListItem = propReactionListItem || contextReactionListItem;
95+
const ReactionListCountItem = propReactionListCountItem || contextReactionListCountItem;
96+
8297
const styles = useStyles({ alignment });
8398

8499
const supportedReactionTypes = supportedReactions?.map(

package/src/components/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ export * from './Message/MessageSimple/MessageTextContainer';
106106
export * from './Message/MessageSimple/MessageTimestamp';
107107
export * from './Message/MessageSimple/ReactionList/ReactionListBottom';
108108
export * from './Message/MessageSimple/ReactionList/ReactionListTop';
109+
export * from './Message/MessageSimple/ReactionList/ReactionListClustered';
110+
export * from './Message/MessageSimple/ReactionList/ReactionListItem';
111+
export * from './Message/MessageSimple/ReactionList/ReactionListItemWrapper';
109112
export * from './Message/MessageSimple/utils/renderText';
110113
export * from './Message/utils/messageActions';
111114
export * from '../utils/removeReservedFields';

package/src/contexts/messagesContext/MessagesContext.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ import type { MessageStatusProps } from '../../components/Message/MessageSimple/
4949
import type { MessageTextProps } from '../../components/Message/MessageSimple/MessageTextContainer';
5050
import { MessageTimestampProps } from '../../components/Message/MessageSimple/MessageTimestamp';
5151
import { ReactionListBottomProps } from '../../components/Message/MessageSimple/ReactionList/ReactionListBottom';
52+
import { ReactionListClusteredProps } from '../../components/Message/MessageSimple/ReactionList/ReactionListClustered';
53+
import {
54+
ReactionListItemProps,
55+
ReactionListCountItemProps,
56+
} from '../../components/Message/MessageSimple/ReactionList/ReactionListItem';
57+
import { ReactionListItemWrapperProps } from '../../components/Message/MessageSimple/ReactionList/ReactionListItemWrapper';
5258
import type { ReactionListTopProps } from '../../components/Message/MessageSimple/ReactionList/ReactionListTop';
5359
import type { MarkdownRules } from '../../components/Message/MessageSimple/utils/renderText';
5460
import type { MessageActionsParams } from '../../components/Message/utils/messageActions';
@@ -608,6 +614,25 @@ export type MessagesContextValue = Pick<MessageContextValue, 'isMessageAIGenerat
608614
*/
609615
ReactionListTop?: React.ComponentType<ReactionListTopProps>;
610616

617+
/**
618+
* UI component for ReactionListBottom
619+
* Defaults to: [ReactionList](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/Reaction/ReactionList.tsx)
620+
*/
621+
ReactionListClustered: React.ComponentType<ReactionListClusteredProps>;
622+
/**
623+
* UI component for ReactionListSegmented
624+
* Defaults to: [ReactionList](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/Reaction/ReactionList.tsx)
625+
*/
626+
ReactionListItem: React.ComponentType<ReactionListItemProps>;
627+
628+
/**
629+
* UI component for ReactionListItemWrapper
630+
* Defaults to: [ReactionListItemWrapper](https://github.com/GetStream/stream-chat-react-native/blob/main/package/src/components/Reaction/ReactionListItemWrapper.tsx)
631+
*/
632+
ReactionListItemWrapper: React.ComponentType<ReactionListItemWrapperProps>;
633+
634+
ReactionListCountItem: React.ComponentType<ReactionListCountItemProps>;
635+
611636
/**
612637
* Full override of the reaction function on Message and Message Overlay
613638
*

0 commit comments

Comments
 (0)