1- import React , { type ComponentPropsWithoutRef , useState } from 'react' ;
1+ import React , { type ComponentPropsWithoutRef , useMemo , useState } from 'react' ;
22import clsx from 'clsx' ;
33
44import type { ReactionsListModalProps } from './ReactionsListModal' ;
@@ -53,6 +53,9 @@ export type ReactionsListProps = Partial<
5353 visualStyle ?: 'clustered' | 'segmented' | null ;
5454} ;
5555
56+ /**
57+ * Renders a button if `buttonIf` is true, otherwise renders a fragment. No props but children are passed to fragment, but all props are passed to button if it's rendered.
58+ */
5659const FragmentOrButton = ( {
5760 buttonIf : renderButton = false ,
5861 children,
@@ -77,22 +80,40 @@ const UnMemoizedReactionsList = (props: ReactionsListProps) => {
7780 ...rest
7881 } = props ;
7982
80- const { existingReactions, hasReactions, totalReactionCount } =
83+ const { existingReactions, hasReactions, totalReactionCount, uniqueReactionTypeCount } =
8184 useProcessReactions ( rest ) ;
8285 const [ selectedReactionType , setSelectedReactionType ] = useState < ReactionType | null > (
8386 null ,
8487 ) ;
8588 const { t } = useTranslationContext ( 'ReactionsList' ) ;
8689 const { ReactionsListModal = DefaultReactionsListModal } = useComponentContext ( ) ;
8790
88- const handleReactionButtonClick = ( reactionType : ReactionType ) => {
91+ const handleReactionButtonClick = ( reactionType : ReactionType | null ) => {
8992 if ( totalReactionCount > MAX_MESSAGE_REACTIONS_TO_FETCH ) {
9093 return ;
9194 }
9295
9396 setSelectedReactionType ( reactionType ) ;
9497 } ;
9598
99+ /**
100+ * In segmented style with top position we show max 4 reactions and a
101+ * count of the rest, so we need to cap the existing reactions to display
102+ * at 4 and calculate the count of the rest.
103+ */
104+ const cappedExistingReactions = useMemo ( ( ) => {
105+ if ( visualStyle !== 'segmented' || verticalPosition !== 'top' ) return null ;
106+
107+ const sliced = existingReactions . slice ( 0 , 4 ) ;
108+ return {
109+ reactionCountToDisplay : sliced . reduce (
110+ ( accumulatedCount , { reactionCount } ) => accumulatedCount + reactionCount ,
111+ 0 ,
112+ ) ,
113+ reactionsToDisplay : sliced ,
114+ } ;
115+ } , [ existingReactions , verticalPosition , visualStyle ] ) ;
116+
96117 if ( ! hasReactions ) return null ;
97118
98119 return (
@@ -116,7 +137,7 @@ const UnMemoizedReactionsList = (props: ReactionsListProps) => {
116137 }
117138 >
118139 < ul className = 'str-chat__message-reactions__list' >
119- { existingReactions . map (
140+ { ( cappedExistingReactions ?. reactionsToDisplay ?? existingReactions ) . map (
120141 ( { EmojiComponent, reactionCount, reactionType } ) =>
121142 EmojiComponent && (
122143 < li
@@ -131,7 +152,7 @@ const UnMemoizedReactionsList = (props: ReactionsListProps) => {
131152 < span className = 'str-chat__message-reactions__item-icon' >
132153 < EmojiComponent />
133154 </ span >
134- { visualStyle === 'segmented' && (
155+ { visualStyle === 'segmented' && reactionCount > 1 && (
135156 < span
136157 className = 'str-chat__message-reactions__item-count'
137158 data-testclass = 'message-reactions-item-count'
@@ -143,6 +164,20 @@ const UnMemoizedReactionsList = (props: ReactionsListProps) => {
143164 </ li >
144165 ) ,
145166 ) }
167+ { uniqueReactionTypeCount > 4 && cappedExistingReactions && (
168+ < li className = 'str-chat__message-reactions__list-item str-chat__message-reactions__list-item--more' >
169+ < button
170+ className = 'str-chat__message-reactions__list-item-button'
171+ onClick = { ( ) =>
172+ handleReactionButtonClick (
173+ existingReactions . at ( - 1 ) ?. reactionType ?? null ,
174+ )
175+ }
176+ >
177+ +{ totalReactionCount - cappedExistingReactions . reactionCountToDisplay }
178+ </ button >
179+ </ li >
180+ ) }
146181 </ ul >
147182 { visualStyle === 'clustered' && (
148183 < span className = 'str-chat__message-reactions__total-count' >
0 commit comments