diff --git a/src/components/Message/MessageRepliesCountButton.tsx b/src/components/Message/MessageRepliesCountButton.tsx
index 8fdcda56c..e1019d214 100644
--- a/src/components/Message/MessageRepliesCountButton.tsx
+++ b/src/components/Message/MessageRepliesCountButton.tsx
@@ -1,5 +1,7 @@
import type { MouseEventHandler } from 'react';
-import React from 'react';
+import type { UserResponse } from 'stream-chat';
+import React, { useMemo } from 'react';
+
import { useTranslationContext } from '../../context/TranslationContext';
import { useChannelStateContext, useComponentContext } from '../../context';
import { AvatarStack as DefaultAvatarStack } from '../Avatar';
@@ -13,23 +15,39 @@ export type MessageRepliesCountButtonProps = {
onClick?: MouseEventHandler;
/* The amount of replies (i.e., threaded messages) on a message */
reply_count?: number;
+ thread_participants?: UserResponse[];
};
function UnMemoizedMessageRepliesCountButton(props: MessageRepliesCountButtonProps) {
const { AvatarStack = DefaultAvatarStack } = useComponentContext(
MessageRepliesCountButton.name,
);
- const { labelPlural, labelSingle, onClick, reply_count = 0 } = props;
+ const {
+ labelPlural,
+ labelSingle,
+ onClick,
+ reply_count: replyCount = 0,
+ thread_participants: threadParticipants = [],
+ } = props;
const { channelCapabilities } = useChannelStateContext();
const { t } = useTranslationContext('MessageRepliesCountButton');
- if (!reply_count) return null;
+ const avatarStackDisplayInfo = useMemo(
+ () =>
+ threadParticipants.slice(0, 3).map((participant) => ({
+ imageUrl: participant.image,
+ userName: participant.name || participant.id,
+ })),
+ [threadParticipants],
+ );
+
+ if (!replyCount) return null;
- let replyCountText = t('replyCount', { count: reply_count });
+ let replyCountText = t('replyCount', { count: replyCount });
- if (labelPlural && reply_count > 1) {
- replyCountText = `${reply_count} ${labelPlural}`;
+ if (labelPlural && replyCount > 1) {
+ replyCountText = `${replyCount} ${labelPlural}`;
} else if (labelSingle) {
replyCountText = `1 ${labelSingle}`;
}
@@ -44,21 +62,7 @@ function UnMemoizedMessageRepliesCountButton(props: MessageRepliesCountButtonPro
>
{replyCountText}
-
+
);
diff --git a/src/components/Message/MessageSimple.tsx b/src/components/Message/MessageSimple.tsx
index 301ddcd91..950c3cbeb 100644
--- a/src/components/Message/MessageSimple.tsx
+++ b/src/components/Message/MessageSimple.tsx
@@ -212,14 +212,15 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
)}
+ {showReplyCountButton && (
+
+ )}
+ {showIsReplyInChannel && }
- {showReplyCountButton && (
-
- )}
- {showIsReplyInChannel && }
{showMetadata && (
diff --git a/src/components/Message/styling/Message.scss b/src/components/Message/styling/Message.scss
index 5800315b7..c0181d4b7 100644
--- a/src/components/Message/styling/Message.scss
+++ b/src/components/Message/styling/Message.scss
@@ -75,6 +75,7 @@
--str-chat__message-reminder-border-inline-end: none;
--str-chat__message-reminder-box-shadow: none;
--str-chat__message-reminder-border-radius: 0;
+ --str-chat__message-reactions-host-offset-x: -6px;
/* The maximum allowed width of the message component */
--str-chat__message-max-width: calc(var(--str-chat__spacing-px) * 480);
@@ -210,7 +211,6 @@
grid-template-areas:
'. message-reminder'
'avatar message'
- 'avatar replies'
'avatar translation-notice'
'avatar custom-metadata'
'avatar metadata';
@@ -220,10 +220,18 @@
.str-chat__message-inner {
.str-chat__message-reactions-host {
- justify-content: flex-start;
+ justify-self: flex-start;
&:has(.str-chat__message-reactions--flipped-horizontally) {
- justify-content: flex-end;
+ justify-self: flex-end;
+ }
+
+ &:has(.str-chat__message-reactions--top) {
+ margin-left: var(--str-chat__message-reactions-host-offset-x);
+
+ &:has(.str-chat__message-reactions--flipped-horizontally) {
+ margin-right: var(--str-chat__message-reactions-host-offset-x);
+ }
}
}
}
@@ -233,7 +241,6 @@
grid-template-areas:
'message-reminder .'
'message avatar'
- 'replies avatar'
'translation-notice avatar'
'custom-metadata avatar'
'metadata avatar';
@@ -243,10 +250,18 @@
.str-chat__message-inner {
.str-chat__message-reactions-host {
- justify-content: flex-end;
+ justify-self: flex-end;
&:has(.str-chat__message-reactions--flipped-horizontally) {
- justify-content: flex-start;
+ justify-self: flex-start;
+ }
+
+ &:has(.str-chat__message-reactions--top) {
+ margin-right: var(--str-chat__message-reactions-host-offset-x);
+
+ &:has(.str-chat__message-reactions--flipped-horizontally) {
+ margin-left: var(--str-chat__message-reactions-host-offset-x);
+ }
}
}
}
@@ -279,7 +294,8 @@
display: grid;
grid-template-areas:
'reactions .'
- 'message-bubble options';
+ 'message-bubble options'
+ 'replies replies';
grid-template-columns: auto 1fr;
column-gap: var(--str-chat__spacing-2);
position: relative;
@@ -287,7 +303,6 @@
.str-chat__message-reactions-host {
display: flex;
grid-area: reactions;
- min-width: 100%;
z-index: 1;
&:has(.str-chat__message-reactions--top) {
@@ -306,6 +321,7 @@
&:has(.str-chat__message-reactions--bottom) {
grid-template-areas:
'message-bubble options'
+ 'replies replies'
'reactions .';
}
@@ -338,7 +354,8 @@
grid-template-areas:
'reminder reminder'
'. reactions'
- 'options message-bubble';
+ 'options message-bubble'
+ 'replies replies';
grid-template-columns: 1fr auto;
.str-chat__message-options {
@@ -349,6 +366,7 @@
grid-template-areas:
'reminder reminder'
'options message-bubble'
+ 'replies replies'
'. reactions';
}
}
diff --git a/src/components/Message/styling/MessageRepliesCountButton.scss b/src/components/Message/styling/MessageRepliesCountButton.scss
index 1339f7ce9..dfb398e22 100644
--- a/src/components/Message/styling/MessageRepliesCountButton.scss
+++ b/src/components/Message/styling/MessageRepliesCountButton.scss
@@ -50,6 +50,8 @@
// TODO: connector styling should be defined here but applied in Message.scss
.str-chat__message.str-chat__message--me {
.str-chat__message-replies-count-button-wrapper {
+ justify-self: flex-end;
+
.str-chat__message-replies-count-button {
flex-direction: row;
}
@@ -70,6 +72,8 @@
.str-chat__message.str-chat__message--other {
.str-chat__message-replies-count-button-wrapper {
+ justify-self: flex-start;
+
.str-chat__message-replies-count-button {
flex-direction: row-reverse;
}
diff --git a/src/components/Reactions/ReactionsList.tsx b/src/components/Reactions/ReactionsList.tsx
index 9944e6d5f..54d401755 100644
--- a/src/components/Reactions/ReactionsList.tsx
+++ b/src/components/Reactions/ReactionsList.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { type ComponentPropsWithoutRef, useState } from 'react';
import clsx from 'clsx';
import type { ReactionsListModalProps } from './ReactionsListModal';
@@ -53,6 +53,18 @@ export type ReactionsListProps = Partial<
visualStyle?: 'clustered' | 'segmented' | null;
};
+const FragmentOrButton = ({
+ buttonIf: renderButton = false,
+ children,
+ ...props
+}: ComponentPropsWithoutRef<'button'> & { buttonIf?: boolean }) => {
+ if (renderButton) {
+ return
;
+ }
+
+ return <>{children}>;
+};
+
const UnMemoizedReactionsList = (props: ReactionsListProps) => {
const {
flipHorizontalPosition = false,
@@ -96,36 +108,48 @@ const UnMemoizedReactionsList = (props: ReactionsListProps) => {
})}
role='figure'
>
-
- {existingReactions.map(
- ({ EmojiComponent, reactionCount, reactionType }) =>
- EmojiComponent && (
- -
-
-
- ),
+ {visualStyle === 'segmented' && (
+
+ {reactionCount}
+
+ )}
+
+
+ ),
+ )}
+
+ {visualStyle === 'clustered' && (
+
+ {totalReactionCount}
+
)}
-
- {visualStyle === 'clustered' && (
-
- {totalReactionCount}
-
- )}
+
{selectedReactionType !== null && (