Skip to content

Commit 2412531

Browse files
Add ReactionsList overflow
1 parent e6a5b0e commit 2412531

3 files changed

Lines changed: 63 additions & 10 deletions

File tree

src/components/Reactions/ReactionsList.tsx

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { type ComponentPropsWithoutRef, useState } from 'react';
1+
import React, { type ComponentPropsWithoutRef, useMemo, useState } from 'react';
22
import clsx from 'clsx';
33

44
import 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+
*/
5659
const 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'>

src/components/Reactions/hooks/useProcessReactions.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ export const useProcessReactions = (params: UseProcessReactionsParams) => {
6363
[reactionOptions],
6464
);
6565

66+
/**
67+
* Amount of unique reaction types ("haha", "like", etc.) on a message.
68+
*/
69+
const uniqueReactionTypeCount = useMemo(() => {
70+
if (!reactionGroups) {
71+
return 0;
72+
}
73+
74+
return Object.keys(reactionGroups).filter((reactionType) =>
75+
isSupportedReaction(reactionType),
76+
).length;
77+
}, [isSupportedReaction, reactionGroups]);
78+
6679
const getLatestReactedUserNames = useCallback(
6780
(reactionType?: string) =>
6881
latestReactions?.flatMap((reaction) => {
@@ -125,5 +138,6 @@ export const useProcessReactions = (params: UseProcessReactionsParams) => {
125138
existingReactions,
126139
hasReactions,
127140
totalReactionCount,
128-
};
141+
uniqueReactionTypeCount,
142+
} as const;
129143
};

src/components/Reactions/styling/ReactionList.scss

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
user-select: none;
55
display: flex;
66
color: var(--reaction-text);
7-
font-feature-settings:
8-
'liga' off,
9-
'clig' off;
107
font-family: var(--typography-font-family-sans);
118
font-size: var(--typography-font-size-xxs);
129
font-style: normal;
@@ -26,6 +23,11 @@
2623
}
2724
}
2825

26+
.str-chat__message-reactions__list-item-button {
27+
// temporary hacky fix
28+
min-height: 26px;
29+
}
30+
2931
.str-chat__message-reactions__list-button,
3032
.str-chat__message-reactions__list-item-button {
3133
@include utils.unset-button;
@@ -41,12 +43,14 @@
4143
border: 1px solid var(--reaction-border);
4244
background: var(--reaction-bg);
4345
box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.16);
46+
font-size: inherit;
47+
line-height: 1;
4448

4549
.str-chat__message-reactions__item-icon {
4650
// FIXME: ridiculous hack so that the emoji block is in a square container (1/1 ratio)
4751
font-size: 13px;
4852
line-height: 16px;
49-
font-family: system-ui, sans-serif;
53+
font-family: system-ui;
5054
font-style: normal;
5155
letter-spacing: 0;
5256
display: flex;

0 commit comments

Comments
 (0)