Skip to content

Commit becee89

Browse files
authored
feature: Support reactions in the SuperGroupChannel (#1029)
### ChangeLog & Feature * Support reaction feature in the SuperGroupChannel ### Internal change * modify the `getIsReactionEnabled`
1 parent 6a5f169 commit becee89

File tree

12 files changed

+139
-155
lines changed

12 files changed

+139
-155
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@
6868
},
6969
"dependencies": {
7070
"@sendbird/chat": "^4.11.0",
71-
"@sendbird/react-uikit-message-template-view": "0.0.1-alpha.65",
72-
"@sendbird/uikit-tools": "0.0.1-alpha.65",
71+
"@sendbird/react-uikit-message-template-view": "0.0.1-alpha.68",
72+
"@sendbird/uikit-tools": "0.0.1-alpha.68",
7373
"css-vars-ponyfill": "^2.3.2",
7474
"date-fns": "^2.16.1",
7575
"dompurify": "^3.0.1"

src/lib/Sendbird.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ const SendbirdSDK = ({
396396
enableTypingIndicator: configs.groupChannel.channel.enableTypingIndicator,
397397
enableDocument: configs.groupChannel.channel.input.enableDocument,
398398
enableReactions: sdkInitialized && configsWithAppAttr(sdk).groupChannel.channel.enableReactions,
399+
enableReactionsSupergroup: sdkInitialized && configsWithAppAttr(sdk).groupChannel.channel.enableReactionsSupergroup,
399400
replyType: configs.groupChannel.channel.replyType,
400401
threadReplySelectType: getCaseResolvedThreadReplySelectType(configs.groupChannel.channel.threadReplySelectType).lowerCase,
401402
typingIndicatorTypes: configs.groupChannel.channel.typingIndicatorTypes,

src/lib/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ export interface SendBirdStateConfig {
103103
enableTypingIndicator: SBUConfig['groupChannel']['channel']['enableTypingIndicator'];
104104
enableDocument: SBUConfig['groupChannel']['channel']['input']['enableDocument'];
105105
enableReactions: SBUConfig['groupChannel']['channel']['enableReactions'];
106+
enableReactionsSupergroup: SBUConfig['groupChannel']['channel']['enableReactionsSupergroup'];
106107
replyType: SBUConfig['groupChannel']['channel']['replyType'];
107108
threadReplySelectType: SBUConfig['groupChannel']['channel']['threadReplySelectType'];
108109
typingIndicatorTypes: SBUConfig['groupChannel']['channel']['typingIndicatorTypes'];

src/modules/Channel/context/ChannelProvider.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,9 @@ const ChannelProvider: React.FC<ChannelContextProps> = (props: ChannelContextPro
249249
typingMembers,
250250
} = messagesStore;
251251

252-
const isSuper = currentGroupChannel?.isSuper || false;
253-
const isBroadcast = currentGroupChannel?.isBroadcast || false;
254252
const usingReaction = getIsReactionEnabled({
255-
isBroadcast,
256-
isSuper,
257-
globalLevel: config?.isReactionEnabled,
253+
channel: currentGroupChannel,
254+
config,
258255
moduleLevel: isReactionEnabled,
259256
});
260257

src/modules/GroupChannel/context/GroupChannelProvider.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import PUBSUB_TOPICS, { PubSubSendMessagePayload } from '../../../lib/pubSub/top
2727
import { PubSubTypes } from '../../../lib/pubSub';
2828
import { useMessageActions } from './hooks/useMessageActions';
2929
import { usePreventDuplicateRequest } from './hooks/usePreventDuplicateRequest';
30+
import { getIsReactionEnabled } from '../../../utils/getIsReactionEnabled';
3031

3132
type OnBeforeHandler<T> = (params: T) => T | Promise<T>;
3233
type MessageListQueryParamsType = Omit<MessageCollectionParams, 'filter'> & MessageFilterParams;
@@ -162,9 +163,10 @@ export const GroupChannelProvider = (props: GroupChannelProviderProps) => {
162163
if (replyType === 'NONE') return ChatReplyType.NONE;
163164
return ChatReplyType.ONLY_REPLY_TO_CHANNEL;
164165
});
165-
const isReactionEnabled = useIIFE(() => {
166-
if (!currentChannel || currentChannel.isSuper || currentChannel.isBroadcast || currentChannel.isEphemeral) return false;
167-
return moduleReactionEnabled ?? config.groupChannel.enableReactions;
166+
const isReactionEnabled = getIsReactionEnabled({
167+
channel: currentChannel,
168+
config,
169+
moduleLevel: moduleReactionEnabled,
168170
});
169171
const nicknamesMap = useMemo(
170172
() => new Map((currentChannel?.members ?? []).map(({ userId, nickname }) => [userId, nickname])),

src/modules/Thread/components/ParentMessageInfo/ParentMessageInfoItem.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ export default function ParentMessageInfoItem({
8181
// Emoji reactions
8282
const isReactionActivated = isReactionEnabled
8383
&& replyType === 'THREAD'
84-
&& !currentChannel?.isSuper
85-
&& !currentChannel?.isBroadcast
8684
&& message?.reactions?.length > 0;
8785

8886
const tokens = useMemo(() => {

src/modules/Thread/components/ParentMessageInfo/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ export default function ParentMessageInfo({
6767
const [supposedHover, setSupposedHover] = useState(false);
6868
const [showFileViewer, setShowFileViewer] = useState(false);
6969
const usingReaction = getIsReactionEnabled({
70-
globalLevel: isReactionEnabled,
71-
isSuper: currentChannel.isSuper,
72-
isBroadcast: currentChannel.isBroadcast,
70+
channel: currentChannel,
71+
config,
72+
moduleLevel: isReactionEnabled,
7373
});
7474
const isByMe = userId === parentMessage.sender.userId;
7575

src/modules/Thread/components/ThreadList/ThreadListItem.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ export default function ThreadListItem({
6969
const [showRemove, setShowRemove] = useState(false);
7070
const [showFileViewer, setShowFileViewer] = useState(false);
7171
const usingReaction = getIsReactionEnabled({
72-
globalLevel: isReactionEnabled,
73-
isSuper: currentChannel.isSuper,
74-
isBroadcast: currentChannel.isBroadcast,
72+
channel: currentChannel,
73+
config,
74+
moduleLevel: isReactionEnabled,
7575
});
7676

7777
// Move to message

src/ui/EmojiReactions/index.tsx

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import './index.scss';
22
import React, { ReactElement, useRef, useState } from 'react';
3-
import type { Emoji, EmojiContainer } from '@sendbird/chat';
3+
import type { Emoji, EmojiContainer, User } from '@sendbird/chat';
44
import type { Reaction } from '@sendbird/chat/message';
55
import type { GroupChannel } from '@sendbird/chat/groupChannel';
66

@@ -17,7 +17,8 @@ import ReactionItem from './ReactionItem';
1717
import { useMediaQueryContext } from '../../lib/MediaQueryContext';
1818
import { AddReactionBadgeItem } from './AddReactionBadgeItem';
1919
import { MobileEmojisBottomSheet } from '../MobileMenu/MobileEmojisBottomSheet';
20-
import { User } from '@sendbird/chat';
20+
import useSendbirdStateContext from '../../hooks/useSendbirdStateContext';
21+
import { getIsReactionEnabled } from '../../utils/getIsReactionEnabled';
2122

2223
export interface EmojiReactionsProps {
2324
className?: string | Array<string>;
@@ -44,6 +45,17 @@ const EmojiReactions = ({
4445
toggleReaction,
4546
onPressUserProfile,
4647
}: EmojiReactionsProps): ReactElement => {
48+
let showTheReactedMembers = false;
49+
try {
50+
const { config } = useSendbirdStateContext();
51+
showTheReactedMembers = getIsReactionEnabled({
52+
channel,
53+
config,
54+
});
55+
} catch (err) {
56+
// TODO: Handle error
57+
}
58+
4759
const { isMobile } = useMediaQueryContext();
4860
const addReactionRef = useRef(null);
4961
const [showEmojiList, setShowEmojiList] = useState(false);
@@ -157,18 +169,20 @@ const EmojiReactions = ({
157169
toggleReaction={toggleReaction}
158170
/>
159171
)}
160-
{(isMobile && selectedEmojiKey && channel !== null) && (
161-
<ReactedMembersBottomSheet
162-
message={message}
163-
channel={channel}
164-
emojiKey={selectedEmojiKey}
165-
hideMenu={() => {
166-
setSelectedEmojiKey('');
167-
}}
168-
emojiContainer={emojiContainer}
169-
onPressUserProfileHandler={onPressUserProfile}
170-
/>
171-
)}
172+
{
173+
(isMobile && selectedEmojiKey && channel !== null && showTheReactedMembers) && (
174+
<ReactedMembersBottomSheet
175+
message={message}
176+
channel={channel}
177+
emojiKey={selectedEmojiKey}
178+
hideMenu={() => {
179+
setSelectedEmojiKey('');
180+
}}
181+
emojiContainer={emojiContainer}
182+
onPressUserProfileHandler={onPressUserProfile}
183+
/>
184+
)
185+
}
172186
</div>
173187
);
174188
};
Lines changed: 67 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,90 @@
1-
import { getIsReactionEnabled } from '../getIsReactionEnabled';
1+
import type { GroupChannel } from '@sendbird/chat/groupChannel';
22

3-
describe('Global-utils/getIsReactionEnabled', () => {
4-
it('should enable as a default', () => {
5-
expect(getIsReactionEnabled({})).toBeTrue();
3+
import type { SendBirdStateConfig } from '../../lib/types';
4+
import { getIsReactionEnabled } from '../getIsReactionEnabled';
65

7-
expect(getIsReactionEnabled({
8-
globalLevel: true,
9-
})).toBeTrue();
10-
expect(getIsReactionEnabled({
11-
moduleLevel: true,
12-
})).toBeTrue();
6+
const normalGroupChannel = (props?) => ({
7+
isBroadcast: false,
8+
isEphemeral: false,
9+
isSuper: false,
10+
...props,
11+
} as GroupChannel);
12+
const normalConfigs = (props?, groupChannelProps?) => ({
13+
groupChannel: {
14+
enableReactions: true,
15+
enableReactionsSupergroup: false,
16+
...groupChannelProps,
17+
},
18+
...props,
19+
} as SendBirdStateConfig);
1320

14-
expect(getIsReactionEnabled({
15-
globalLevel: true,
16-
moduleLevel: true,
17-
})).toBeTrue();
21+
describe('Global-utils/getIsReactionEnabled', () => {
22+
it('should prioritize the moduleLevel than global config', () => {
23+
const moduleLevel = true;
24+
expect(getIsReactionEnabled({
25+
channel: normalGroupChannel(),
26+
config: normalConfigs(),
27+
moduleLevel,
28+
})).toBe(moduleLevel);
29+
const moduleLevel2 = false;
30+
expect(getIsReactionEnabled({
31+
channel: normalGroupChannel(),
32+
config: normalConfigs(),
33+
moduleLevel: moduleLevel2,
34+
})).toBe(moduleLevel2);
1835
});
1936

20-
it('should disable if set values are false', () => {
21-
expect(getIsReactionEnabled({
22-
globalLevel: false,
23-
})).toBeFalse();
24-
expect(getIsReactionEnabled({
25-
moduleLevel: false,
26-
})).toBeFalse();
27-
37+
it('should prioritize the isSuper than moduleLevel', () => {
38+
const isSuper = true;
2839
expect(getIsReactionEnabled({
29-
globalLevel: false,
30-
moduleLevel: false,
31-
})).toBeFalse();
32-
});
33-
34-
it('should have higher priority to the moduleLevel than globalLevel', () => {
40+
channel: normalGroupChannel({ isSuper }),
41+
config: normalConfigs(),
42+
moduleLevel: true,
43+
})).toBe(false);
3544
expect(getIsReactionEnabled({
36-
globalLevel: true,
45+
channel: normalGroupChannel({ isSuper }),
46+
config: normalConfigs(),
3747
moduleLevel: false,
38-
})).toBeFalse();
39-
expect(getIsReactionEnabled({
40-
globalLevel: false,
41-
moduleLevel: true,
42-
})).toBeTrue();
48+
})).toBe(false);
4349
});
4450

45-
it('should disable in the special type channels', () => {
46-
expect(getIsReactionEnabled({
47-
globalLevel: true,
48-
isBroadcast: true,
49-
})).toBeFalse();
50-
expect(getIsReactionEnabled({
51-
globalLevel: true,
52-
isSuper: true,
53-
})).toBeFalse();
54-
expect(getIsReactionEnabled({
55-
globalLevel: true,
56-
isBroadcast: true,
57-
isSuper: true,
58-
})).toBeFalse();
59-
60-
expect(getIsReactionEnabled({
61-
moduleLevel: true,
62-
isBroadcast: true,
63-
})).toBeFalse();
64-
expect(getIsReactionEnabled({
65-
moduleLevel: true,
66-
isSuper: true,
67-
})).toBeFalse();
68-
expect(getIsReactionEnabled({
69-
moduleLevel: true,
70-
isBroadcast: true,
71-
isSuper: true,
72-
})).toBeFalse();
73-
51+
it('should prioritize moduleLevel than enableReactionsSupergroup', () => {
52+
const isSuper = true;
7453
expect(getIsReactionEnabled({
75-
globalLevel: true,
54+
channel: normalGroupChannel({ isSuper }),
55+
config: normalConfigs({}, { enableReactionsSupergroup: true }),
7656
moduleLevel: true,
77-
isBroadcast: true,
78-
})).toBeFalse();
57+
})).toBe(true);
7958
expect(getIsReactionEnabled({
80-
globalLevel: true,
81-
moduleLevel: true,
82-
isSuper: true,
83-
})).toBeFalse();
84-
expect(getIsReactionEnabled({
85-
globalLevel: true,
86-
moduleLevel: true,
87-
isBroadcast: true,
88-
isSuper: true,
89-
})).toBeFalse();
59+
channel: normalGroupChannel({ isSuper }),
60+
config: normalConfigs({}, { enableReactionsSupergroup: true }),
61+
moduleLevel: false,
62+
})).toBe(false);
9063
});
9164

92-
it('should disable if only one special channel type is true', () => {
93-
expect(getIsReactionEnabled({
94-
globalLevel: true,
95-
moduleLevel: true,
96-
isBroadcast: false,
97-
isSuper: true,
98-
})).toBeFalse();
65+
it('should be false when it is broadcast or ephemeral channel', () => {
66+
const isBroadcast = true;
9967
expect(getIsReactionEnabled({
100-
globalLevel: true,
68+
channel: normalGroupChannel({ isBroadcast }),
69+
config: normalConfigs(),
10170
moduleLevel: true,
102-
isBroadcast: true,
103-
isSuper: false,
104-
})).toBeFalse();
71+
})).toBe(false);
10572
expect(getIsReactionEnabled({
106-
globalLevel: true,
107-
isBroadcast: false,
108-
isSuper: true,
109-
})).toBeFalse();
73+
channel: normalGroupChannel({ isBroadcast }),
74+
config: normalConfigs(),
75+
moduleLevel: false,
76+
})).toBe(false);
77+
78+
const isEphemeral = true;
11079
expect(getIsReactionEnabled({
80+
channel: normalGroupChannel({ isEphemeral }),
81+
config: normalConfigs(),
11182
moduleLevel: true,
112-
isBroadcast: true,
113-
isSuper: false,
114-
})).toBeFalse();
83+
})).toBe(false);
11584
expect(getIsReactionEnabled({
116-
isBroadcast: false,
117-
isSuper: true,
118-
})).toBeFalse();
119-
expect(getIsReactionEnabled({
120-
isBroadcast: true,
121-
isSuper: false,
122-
})).toBeFalse();
85+
channel: normalGroupChannel({ isEphemeral }),
86+
config: normalConfigs(),
87+
moduleLevel: false,
88+
})).toBe(false);
12389
});
12490
});

0 commit comments

Comments
 (0)