Skip to content

Commit 3ba2194

Browse files
authored
Fix: [v3] Fix for QA mention and verse (#155)
Fix: UI materials * Change the front-weight of Subtitle2 from 600 to 500 * Modify mention badge position on the ChannelListItem component * Change Info Icon size to 20px on the SuggestedMentionListItem component Fix: Mention related stuff * Modify the onMouseOver event on the SuggestedMentionList component * Filter 'html' text when pasting text to the MessageInput component * Hide and apply ellipsis for overflowing text on the SuggestedMentionListItem component * Deactivate the MessageInput component when the current user is muted or the current channel is frozen * Reset the mention states of the current channel when changing channel and closing the edit MessageInput component
1 parent 193f538 commit 3ba2194

File tree

14 files changed

+113
-42
lines changed

14 files changed

+113
-42
lines changed

scripts/index_d_ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -668,11 +668,11 @@ type SuggestedMentionListProps = {
668668
};
669669

670670
type SuggestedUserMentionItemProps = {
671-
member: SendBird.User;
671+
member: SendBird.User | SendBird.Member;
672672
isFocused?: boolean;
673673
onClick?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
674674
onMouseOver?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
675-
renderUserMentionItem?: (props: { user: SendBird.User }) => JSX.Element;
675+
renderUserMentionItem?: (props: { user: SendBird.User | SendBird.Member }) => JSX.Element;
676676
};
677677

678678
interface UnreadCountProps {

src/index.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,14 @@ interface RemoveMessageProps {
706706
message: EveryMessage;
707707
}
708708

709+
interface SendbirdUIKitUIEvent<> {
710+
event: React.MouseEvent<HTMLDivElement, MouseEvent> | React.KeyboardEvent<HTMLDivElement>;
711+
}
712+
export interface MentionItemUIEvent extends SendbirdUIKitUIEvent {
713+
member: SendBird.Member;
714+
itemRef: React.RefObject<HTMLDivElement>;
715+
}
716+
709717
declare module '@sendbird/uikit-react/Channel' {
710718
type Channel = React.FC<ChannelProps>;
711719
export default Channel;

src/smart-components/App/stories/index.stories.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,12 @@ export const user1 = () => fitPageSize(
224224
profileUrl={addProfile}
225225
showSearchIcon
226226
allowProfileEdit
227-
config={{ logLevel: 'all' }}
227+
config={{
228+
logLevel: 'all',
229+
userMention: {
230+
maxMentionCount: 2,
231+
}
232+
}}
228233
queries={{}}
229234
replyType="QUOTE_REPLY"
230235
isMentionEnabled
@@ -265,6 +270,7 @@ export const user3 = () => fitPageSize(
265270
profileUrl={addProfile}
266271
config={{ logLevel: 'all' }}
267272
replyType="QUOTE_REPLY"
273+
isMentionEnabled
268274
isTypingIndicatorEnabledOnChannelList
269275
isMessageReceiptStatusEnabledOnChannelList
270276
/>

src/smart-components/Channel/components/Message/index.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,13 @@ const Message: React.FC<MessageUIProps> = (props: MessageUIProps) => {
240240
});
241241
setShowEdit(false);
242242
}}
243-
onCancelEdit={() => { setShowEdit(false); }}
243+
onCancelEdit={() => {
244+
setMentionNickname('');
245+
setMentionedUsers([]);
246+
setMentionedUserIds([]);
247+
setMentionSuggestedUsers([])
248+
setShowEdit(false);
249+
}}
244250
onUserMentioned={(user) => {
245251
if (selectedUser?.userId === user?.userId) {
246252
setSelectedUser(null);

src/smart-components/Channel/components/MessageInput/index.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,22 @@ const MessageInputWrapper = (): JSX.Element => {
4444
const isOperator = utils.isOperator(channel);
4545
const { isBroadcast } = channel;
4646

47-
const displaySuggestedMentionList = (isMentionEnabled && mentionNickname.length > 0);
47+
const displaySuggestedMentionList = isOnline
48+
&& isMentionEnabled
49+
&& mentionNickname.length > 0
50+
&& !utils.isDisabledBecauseFrozen(channel)
51+
&& !utils.isDisabledBecauseMuted(channel);
52+
53+
// Reset when channel changes
54+
useEffect(() => {
55+
setMentionNickname('');
56+
setMentionedUsers([]);
57+
setMentionedUserIds([]);
58+
setSelectedUser(null);
59+
setMentionSuggestedUsers([]);
60+
setAbleMention(true);
61+
setMessageInputEvent(null);
62+
}, [channel?.url]);
4863

4964
useEffect(() => {
5065
if (mentionedUsers?.length >= maxUserMentionCount) {

src/smart-components/Channel/components/SuggestedMentionList/SuggestedUserMentionItem.tsx

+20-9
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,47 @@ import Avatar from '../../../../ui/Avatar';
44
import Label, { LabelTypography, LabelColors } from '../../../../ui/Label';
55
import { LocalizationContext } from '../../../../lib/LocalizationContext';
66
import SendBird from 'sendbird';
7+
import { MentionItemUIEvent } from '../../../..';
78

89
interface SuggestedUserMentionItemProps {
9-
member: SendBird.User;
10+
member: SendBird.User | SendBird.Member;
1011
isFocused?: boolean;
11-
onClick?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
12-
onMouseOver?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
13-
renderUserMentionItem?: (props: { user: SendBird.User }) => JSX.Element;
12+
parentScrollRef?: React.RefObject<HTMLDivElement>;
13+
onClick?: (props: MentionItemUIEvent) => void;
14+
onMouseOver?: (props: MentionItemUIEvent) => void;
15+
onMouseMove?: (props: MentionItemUIEvent) => void;
16+
renderUserMentionItem?: (props: { user: SendBird.User | SendBird.Member }) => JSX.Element;
1417
}
1518

1619
function SuggestedUserMentionItem(props: SuggestedUserMentionItemProps): JSX.Element {
1720
const {
1821
member,
1922
isFocused = false,
23+
parentScrollRef,
2024
onClick,
2125
onMouseOver,
26+
onMouseMove,
2227
renderUserMentionItem,
2328
} = props;
2429
const scrollRef = useRef(null);
2530
const { stringSet = {} } = useContext(LocalizationContext);
2631
useEffect(() => {
2732
if (isFocused) {
28-
scrollRef?.current?.scrollIntoView({ block: 'center', inline: 'center' });
33+
if (parentScrollRef?.current?.scrollTop >= scrollRef?.current?.offsetTop) {
34+
scrollRef?.current?.scrollIntoView({ block: "start", inline: "nearest" });
35+
} else if (parentScrollRef?.current?.scrollTop + parentScrollRef?.current?.clientHeight <= scrollRef?.current?.offsetTop) {
36+
scrollRef?.current?.scrollIntoView({ block: "end", inline: "nearest" });
37+
}
2938
}
3039
}, [isFocused]);
3140
const customMentionItem = useMemo(() => {
3241
if (renderUserMentionItem) {
3342
return (
3443
<div
3544
className="sendbird-mention-suggest-list__user-item"
36-
onClick={onClick}
37-
onMouseOver={onMouseOver}
45+
onClick={(event) => onClick?.({ event, member: (member as SendBird.Member), itemRef: scrollRef })}
46+
onMouseOver={(event) => onMouseOver?.({ event, member: (member as SendBird.Member), itemRef: scrollRef })}
47+
onMouseMove={(event) => onMouseMove?.({ event, member: (member as SendBird.Member), itemRef: scrollRef })}
3848
key={member.nickname}
3949
ref={scrollRef}
4050
>
@@ -49,8 +59,9 @@ function SuggestedUserMentionItem(props: SuggestedUserMentionItemProps): JSX.Ele
4959
return (
5060
<div
5161
className={`sendbird-mention-suggest-list__user-item ${isFocused ? 'focused' : ''}`}
52-
onClick={onClick}
53-
onMouseOver={onMouseOver}
62+
onClick={(event) => onClick?.({ event, member: (member as SendBird.Member), itemRef: scrollRef })}
63+
onMouseOver={(event) => onMouseOver?.({ event, member: (member as SendBird.Member), itemRef: scrollRef })}
64+
onMouseMove={(event) => onMouseMove?.({ event, member: (member as SendBird.Member), itemRef: scrollRef })}
5465
key={member.nickname}
5566
ref={scrollRef}
5667
>

src/smart-components/Channel/components/SuggestedMentionList/index.scss

+8-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@
2121
@include themed() {
2222
background-color: t(bg-0);
2323
}
24-
&:hover {
25-
cursor: pointer;
26-
@include themed() {
27-
background-color: t(bg-1);
28-
}
29-
}
3024
&.focused {
3125
@include themed() {
3226
background-color: t(bg-1);
@@ -41,11 +35,19 @@
4135
position: relative;
4236
display: inline-block;
4337
margin-left: 16px;
38+
max-width: calc(100% - 250px);
39+
white-space: nowrap;
40+
overflow: hidden;
41+
text-overflow: ellipsis;
4442
}
4543
.sendbird-mention-suggest-list__user-item__user-id {
4644
position: absolute;
4745
display: inline-block;
4846
right: 16px;
47+
max-width: 180px;
48+
white-space: nowrap;
49+
overflow: hidden;
50+
text-overflow: ellipsis;
4951
}
5052
}
5153

src/smart-components/Channel/components/SuggestedMentionList/index.tsx

+15-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useEffect, useContext } from 'react';
1+
import React, { useState, useEffect, useContext, useRef } from 'react';
22
import SendBird from 'sendbird';
33
import './index.scss';
44

@@ -43,12 +43,12 @@ function SuggestedMentionList(props: SuggestedMentionListProps): JSX.Element {
4343
const { logger } = config;
4444
const currentUserId = stores?.sdkStore?.sdk?.currentUser?.userId || '';
4545
const { currentGroupChannel } = useChannel();
46+
const scrollRef = useRef(null);
4647
const { stringSet } = useContext(LocalizationContext);
4748
const [timer, setTimer] = useState(null);
4849
const [searchString, setSearchString] = useState('');
4950
const [lastSearchString, setLastSearchString] = useState('');
5051
const [currentUser, setCurrentUser] = useState<SendBird.User>(null);
51-
const [mouseOverUser, setMouseOverUser] = useState<SendBird.User>(null);
5252
const [currentMemberList, setCurrentMemberList] = useState<Array<SendBird.Member>>([]);
5353

5454
useEffect(() => {
@@ -88,7 +88,7 @@ function SuggestedMentionList(props: SuggestedMentionListProps): JSX.Element {
8888

8989
/* Fetch member list */
9090
useEffect(() => {
91-
if (!currentGroupChannel || !currentGroupChannel.createMemberListQuery || !ableAddMention) {
91+
if (!currentGroupChannel || !currentGroupChannel.createMemberListQuery) {
9292
logger.warning('SuggestedMentionList: Creating member list query failed');
9393
return;
9494
}
@@ -117,26 +117,28 @@ function SuggestedMentionList(props: SuggestedMentionListProps): JSX.Element {
117117
});
118118
}, [currentGroupChannel?.url, searchString]);
119119

120+
if (!ableAddMention && currentMemberList.length === 0) {
121+
return null;
122+
}
123+
120124
return (
121125
<div
122126
className="sendbird-mention-suggest-list"
123-
onMouseLeave={() => {
124-
if (mouseOverUser) {
125-
setCurrentUser(mouseOverUser);
126-
}
127-
}}
127+
key="sendbird-mention-suggest-list"
128+
ref={scrollRef}
128129
>
129130
{
130131
ableAddMention && currentMemberList?.map((member) => (
131132
<SuggestedUserMentionItem
132133
key={member?.nickname}
133134
member={member}
134135
isFocused={member?.userId === currentUser?.userId}
135-
onClick={() => {
136+
parentScrollRef={scrollRef}
137+
onClick={({ member }) => {
136138
onUserItemClick(member);
137139
}}
138-
onMouseOver={() => {
139-
setMouseOverUser(member);
140+
onMouseOver={({ member }) => {
141+
setCurrentUser(member);
140142
}}
141143
renderUserMentionItem={renderUserMentionItem}
142144
/>
@@ -149,6 +151,8 @@ function SuggestedMentionList(props: SuggestedMentionListProps): JSX.Element {
149151
className="sendbird-mention-suggest-list__notice-item__icon"
150152
type={IconTypes.INFO}
151153
fillColor={IconColors.ON_BACKGROUND_2}
154+
width="20px"
155+
height="20px"
152156
/>
153157
<Label
154158
className="sendbird-mention-suggest-list__notice-item__text"

src/smart-components/Channel/context/utils.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as topics from '../../../lib/pubSub/topics';
55
import {
66
getSendingMessageStatus,
77
getOutgoingMessageStates,
8+
isReadMessage,
89
} from '../../../utils';
910

1011
const MessageStatusType = getOutgoingMessageStates();
@@ -186,7 +187,7 @@ export const getNicknamesMapFromMembers = (members = []) => {
186187

187188
export const getMessageCreatedAt = (message) => format(message.createdAt, 'p');
188189

189-
export const isSameGroup = (message, comparingMessage) => {
190+
export const isSameGroup = (message, comparingMessage, currentChannel) => {
190191
if (!(message
191192
&& comparingMessage
192193
&& message?.messageType !== 'admin'
@@ -200,24 +201,25 @@ export const isSameGroup = (message, comparingMessage) => {
200201
)) {
201202
return false;
202203
}
203-
204204
return (
205205
message?.sendingStatus === comparingMessage?.sendingStatus
206206
&& message?.sender?.userId === comparingMessage?.sender?.userId
207207
&& getMessageCreatedAt(message) === getMessageCreatedAt(comparingMessage)
208+
&& isReadMessage(currentChannel, message) === isReadMessage(currentChannel, comparingMessage)
208209
);
209210
};
210211

211212
export const compareMessagesForGrouping = (
212213
prevMessage,
213214
currMessage,
214215
nextMessage,
216+
currentChannel,
215217
) => {
216218
const sendingStatus = currMessage?.sendingStatus || '';
217219
const isAcceptable = sendingStatus !== 'pending' && sendingStatus !== 'failed';
218220
return [
219-
isSameGroup(prevMessage, currMessage) && isAcceptable,
220-
isSameGroup(currMessage, nextMessage) && isAcceptable,
221+
isSameGroup(prevMessage, currMessage, currentChannel) && isAcceptable,
222+
isSameGroup(currMessage, nextMessage, currentChannel) && isAcceptable,
221223
];
222224
};
223225

src/smart-components/ChannelList/components/ChannelPreview/channel-preview.scss

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@
9191
align-items: center;
9292
margin-left: 8px;
9393
margin-bottom: 12px;
94+
.sendbird-channel-preview__content__lower__unread-message-count__mention {
95+
display: inline-flex;
96+
align-items: center;
97+
margin-right: 4px;
98+
}
9499
}
95100
}
96101
}

src/smart-components/ChannelList/components/ChannelPreview/index.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,14 @@ const ChannelPreview: React.FC<ChannelPreviewInterface> = ({
147147
<div className="sendbird-channel-preview__content__lower__unread-message-count">
148148
{
149149
(isMentionEnabled && channel?.unreadMentionCount > 0)
150-
? <MentionUserLabel color="purple">@</MentionUserLabel>
150+
? (
151+
<MentionUserLabel
152+
className="sendbird-channel-preview__content__lower__unread-message-count__mention"
153+
color="purple"
154+
>
155+
{'@'}
156+
</MentionUserLabel>
157+
)
151158
: null
152159
}
153160
{

src/ui/Label/index.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
.sendbird-label--subtitle-2 {
3535
font-size: 14px;
36-
font-weight: 600;
36+
font-weight: 500;
3737
font-stretch: normal;
3838
font-style: normal;
3939
line-height: 1.14;

src/ui/MessageInput/__tests__/MessageInput.spec.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ describe('MessageInput', () => {
4646
});
4747

4848
it('should display save/cancel button on edit mode', () => {
49+
const messageId = 'aaa';
4950
const component = shallow(
50-
<MessageInput onSendMessage={noop} isEdit />
51+
<MessageInput onSendMessage={noop} isEdit message={{ messageId }} />
5152
);
5253
expect(
53-
component.find('#sendbird-message-input-text-field').exists()
54+
component.find('#sendbird-message-input-text-field' + messageId).exists()
5455
).toBe(true);
5556
expect(
5657
component.find('.sendbird-message-input--send').exists()

src/ui/MessageInput/index.jsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,9 @@ const MessageInput = React.forwardRef((props, ref) => {
347347
])}
348348
>
349349
<div
350-
id={TEXT_FIELD_ID}
350+
id={`${TEXT_FIELD_ID}${isEdit ? message?.messageId : ''}`}
351351
className="sendbird-message-input--textarea"
352-
contentEditable
352+
contentEditable={!disabled}
353353
role="textbox"
354354
aria-label="Text Input"
355355
disabled={disabled}
@@ -360,7 +360,7 @@ const MessageInput = React.forwardRef((props, ref) => {
360360
if (preventEvent) {
361361
e.preventDefault();
362362
} else {
363-
if (!e.shiftKey && e.key === MessageInputKeys.Enter) {
363+
if (!e.shiftKey && e.key === MessageInputKeys.Enter && e?.nativeEvent?.isComposing !== true) {
364364
e.preventDefault();
365365
sendMessage();
366366
}
@@ -390,6 +390,10 @@ const MessageInput = React.forwardRef((props, ref) => {
390390
setIsInput(document?.getElementById?.(TEXT_FIELD_ID)?.innerText?.length > 0);
391391
useMentionedLabelDetection();
392392
}}
393+
onPaste={(e) => {
394+
e.preventDefault();
395+
document.execCommand("insertHTML", false, e?.clipboardData.getData('text'));
396+
}}
393397
/>
394398
{/* placeholder */}
395399
{!isInput && (

0 commit comments

Comments
 (0)