Skip to content

Commit 852490d

Browse files
authored
fix: enable sending reactions to frozen channel with UseFrozenChannel permission (#2097)
* fix: enable sending reactions to frozen channel with UseFrozenChannel permission The prop isReactionEnabled should be used to determine permission to send not view reactions only. * fix: do not use channelConfig.reactions to infer on permission to send reactions
1 parent 959495c commit 852490d

File tree

13 files changed

+211
-107
lines changed

13 files changed

+211
-107
lines changed

docusaurus/docs/React/components/contexts/message-context.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ Function that returns whether or not a message belongs to the current user.
231231
| ------------- |
232232
| () => boolean |
233233

234-
### isReactionEnabled
234+
### isReactionEnabled (deprecated)
235235

236-
If true, reactions are enabled in the currently active channel.
236+
If true, sending reactions is enabled in the currently active channel.
237237

238238
| Type | Default |
239239
| ------- | ------- |

docusaurus/docs/React/components/message-components/message-ui.mdx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,9 +351,8 @@ Function that returns whether a message belongs to the current user (overrides t
351351
| ------------- |
352352
| () => boolean |
353353

354-
### isReactionEnabled
355-
356-
If true, reactions are enabled in the currently active channel (overrides the value stored in `MessageContext`).
354+
### isReactionEnabled (deprecated)
355+
If true, sending reactions is enabled in the currently active channel (overrides the value stored in `MessageContext`).
357356

358357
| Type | Default |
359358
| ------- | ------- |

docusaurus/docs/React/guides/theming/message-ui.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export const CustomMessage = () => {
9191

9292
const messageWrapperRef = useRef(null);
9393

94+
const canReact = isReactionEnabled;
9495
const hasReactions = messageHasReactions(message);
9596
const hasAttachments = message.attachments && message.attachments.length > 0;
9697

@@ -105,13 +106,13 @@ export const CustomMessage = () => {
105106
<MessageTimestamp />
106107
</div>
107108
</div>
108-
{showDetailedReactions && isReactionEnabled && (
109+
{showDetailedReactions && canReact && (
109110
<ReactionSelector ref={reactionSelectorRef} />
110111
)}
111112
<MessageText />
112113
<MessageStatus />
113114
{hasAttachments && <Attachment attachments={message.attachments} />}
114-
{hasReactions && !showDetailedReactions && isReactionEnabled && <SimpleReactionsList />}
115+
{hasReactions && <SimpleReactionsList />}
115116
<MessageRepliesCountButton reply_count={message.reply_count} />
116117
</div>
117118
</div>

docusaurus/docs/React/guides/theming/reactions.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ export const CustomMessage = () => {
166166

167167
const messageWrapperRef = useRef(null);
168168

169+
const canReact = isReactionEnabled;
169170
const hasReactions = messageHasReactions(message);
170171

171172
return (
@@ -179,13 +180,13 @@ export const CustomMessage = () => {
179180
<MessageTimestamp />
180181
</div>
181182
</div>
182-
{showDetailedReactions && isReactionEnabled && (
183+
{showDetailedReactions && canReact && (
183184
<ReactionSelector reactionOptions={customReactions} ref={reactionSelectorRef} />
184185
)}
185186
<MessageText />
186187
<MessageStatus />
187188
{message.attachments && <Attachment attachments={message.attachments} />}
188-
{hasReactions && !showDetailedReactions && isReactionEnabled && (
189+
{hasReactions && (
189190
<SimpleReactionsList reactionOptions={customReactions} />
190191
)}
191192
<MessageRepliesCountButton reply_count={message.reply_count} />

src/components/Message/MessageSimple.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ const MessageSimpleWithContext = <
8484
return <MessageDeleted message={message} />;
8585
}
8686

87+
/** FIXME: isReactionEnabled should be removed with next major version and a proper centralized permissions logic should be put in place
88+
* With the current permissions implementation it would be sth like:
89+
* const messageActions = getMessageActions();
90+
* const canReact = messageActions.includes(MESSAGE_ACTIONS.react);
91+
*/
92+
const canReact = isReactionEnabled;
93+
const canShowReactions = hasReactions;
94+
8795
const showMetadata = !groupedByUser || endOfGroup;
8896
const showReplyCountButton = !threadList && !!message.reply_count;
8997
const allowRetry = message.status === 'failed' && message.errorStatusCode !== 403;
@@ -100,8 +108,7 @@ const MessageSimpleWithContext = <
100108
'pinned-message': message.pinned,
101109
'str-chat__message--has-attachment': hasAttachment,
102110
'str-chat__message--highlighted': highlighted,
103-
'str-chat__message--with-reactions str-chat__message-with-thread-link':
104-
hasReactions && isReactionEnabled,
111+
'str-chat__message--with-reactions str-chat__message-with-thread-link': canShowReactions,
105112
'str-chat__message-send-can-be-retried':
106113
message?.status === 'failed' && message?.errorStatusCode !== 403,
107114
'str-chat__virtual-message__wrapper--end': endOfGroup,
@@ -145,10 +152,8 @@ const MessageSimpleWithContext = <
145152
>
146153
<MessageOptions />
147154
<div className='str-chat__message-reactions-host'>
148-
{hasReactions && isReactionEnabled && <ReactionsList reverse />}
149-
{showDetailedReactions && isReactionEnabled && (
150-
<ReactionSelector ref={reactionSelectorRef} />
151-
)}
155+
{canShowReactions && <ReactionsList reverse />}
156+
{showDetailedReactions && canReact && <ReactionSelector ref={reactionSelectorRef} />}
152157
</div>
153158
<div className='str-chat__message-bubble'>
154159
{message.attachments?.length && !message.quoted_message ? (

src/components/Message/__tests__/Message.test.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ async function renderComponent({
5858

5959
return renderer(
6060
<ChatProvider value={{ client, ...clientOpts }}>
61-
<ChannelStateProvider value={{ channel, ...channelStateOpts }}>
61+
<ChannelStateProvider
62+
value={{ channel, channelCapabilities: { 'send-reaction': true }, ...channelStateOpts }}
63+
>
6264
<ChannelActionProvider
6365
value={{
6466
openThread: jest.fn(),
@@ -174,6 +176,23 @@ describe('<Message /> component', () => {
174176
expect(sendReaction).toHaveBeenCalledWith(message.id, { type: reaction.type });
175177
});
176178

179+
it('should not send reaction without permission', async () => {
180+
const reaction = generateReaction({ user: bob });
181+
const message = generateMessage({ own_reactions: [] });
182+
let context;
183+
184+
await renderComponent({
185+
channelStateOpts: { channelCapabilities: { 'send-reaction': false } },
186+
contextCallback: (ctx) => {
187+
context = ctx;
188+
},
189+
message,
190+
});
191+
192+
await context.handleReaction(reaction.type);
193+
expect(sendReaction).not.toHaveBeenCalled();
194+
});
195+
177196
it('should rollback reaction if channel update fails', async () => {
178197
const reaction = generateReaction({ user: bob });
179198
const message = generateMessage({ own_reactions: [] });

src/components/Message/__tests__/MessageOptions.test.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,17 @@ describe('<MessageOptions />', () => {
169169
const { getByTestId } = await renderMessageOptions({
170170
channelStateOpts: {
171171
channelCapabilities: { 'send-reaction': true },
172-
channelConfig: { reactions: true },
173172
},
174173
});
175174
expect(getByTestId(reactionActionTestId)).toBeInTheDocument();
176175
});
177176

178177
it('should not display reactions action when channel has reactions disabled', async () => {
179-
const { queryByTestId } = await renderMessageOptions({ channelConfig: { reactions: false } });
178+
const { queryByTestId } = await renderMessageOptions({
179+
channelStateOpts: {
180+
channelCapabilities: { 'send-reaction': false },
181+
},
182+
});
180183
expect(queryByTestId(reactionActionTestId)).not.toBeInTheDocument();
181184
});
182185

src/components/Message/__tests__/MessageSimple.test.js

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ import {
2525
ChannelStateProvider,
2626
ChatProvider,
2727
ComponentProvider,
28+
EmojiProvider,
2829
TranslationProvider,
2930
} from '../../../context';
3031
import {
32+
emojiComponentMock,
3133
emojiDataMock,
3234
generateChannel,
3335
generateMessage,
@@ -58,15 +60,16 @@ const retrySendMessageMock = jest.fn();
5860
async function renderMessageSimple({
5961
message,
6062
props = {},
61-
channelConfigOverrides = { reactions: true, replies: true },
63+
channelConfigOverrides = { replies: true },
64+
channelCapabilities = { 'send-reaction': true },
6265
components = {},
6366
renderer = render,
6467
}) {
6568
const channel = generateChannel({
6669
getConfig: () => channelConfigOverrides,
6770
state: { membership: {} },
6871
});
69-
const channelCapabilities = { 'send-reaction': true };
72+
7073
const channelConfig = channel.getConfig();
7174
const client = await getTestClientWithUser(alice);
7275

@@ -87,13 +90,22 @@ async function renderMessageSimple({
8790
...components,
8891
}}
8992
>
90-
<Message
91-
getMessageActions={() => Object.keys(MESSAGE_ACTIONS)}
92-
isMyMessage={() => true}
93-
message={message}
94-
threadList={false}
95-
{...props}
96-
/>
93+
<EmojiProvider
94+
value={{
95+
Emoji: emojiComponentMock.Emoji,
96+
emojiConfig: emojiDataMock,
97+
EmojiIndex: emojiComponentMock.EmojiIndex,
98+
EmojiPicker: emojiComponentMock.EmojiPicker,
99+
}}
100+
>
101+
<Message
102+
getMessageActions={() => Object.keys(MESSAGE_ACTIONS)}
103+
isMyMessage={() => true}
104+
message={message}
105+
threadList={false}
106+
{...props}
107+
/>
108+
</EmojiProvider>
97109
</ComponentProvider>
98110
</TranslationProvider>
99111
</ChannelActionProvider>
@@ -222,18 +234,19 @@ describe('<MessageSimple />', () => {
222234
expect(results).toHaveNoViolations();
223235
});
224236

225-
it('should not render reaction list if reaction is disabled in channel config', async () => {
237+
// FIXME: test relying on deprecated channel config parameter
238+
it('should render reaction list even though sending reactions is disabled in channel config', async () => {
226239
const bobReaction = generateReaction({ user: bob });
227240
const message = generateAliceMessage({
228241
latest_reactions: [bobReaction],
229242
text: undefined,
230243
});
231244

232245
const { container, queryByTestId } = await renderMessageSimple({
233-
channelConfigOverrides: { reactions: false },
246+
channelCapabilities: { 'send-reaction': false },
234247
message,
235248
});
236-
expect(queryByTestId('reaction-list')).not.toBeInTheDocument();
249+
expect(queryByTestId('reaction-list')).toBeInTheDocument();
237250
const results = await axe(container);
238251
expect(results).toHaveNoViolations();
239252
});

0 commit comments

Comments
 (0)