Skip to content

Commit 193f538

Browse files
sravan-sSravan S
and
Sravan S
authored
feat: Mention improvements (#156)
* Show profile on clicking a mention * Visual highlight when user is mention Fixes: https://sendbird.atlassian.net/browse/UIKIT-1786 https://sendbird.atlassian.net/browse/UIKIT-1787 Co-authored-by: Sravan S <[email protected]>
1 parent 95f0a3a commit 193f538

File tree

11 files changed

+155
-24
lines changed

11 files changed

+155
-24
lines changed

exports.js

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export default {
110110
'ui/LinkLabel': 'src/ui/LinkLabel/index.jsx',
111111
'ui/Loader': 'src/ui/Loader/index.jsx',
112112
'ui/MentionUserLabel': 'src/ui/MentionUserLabel/index.tsx',
113+
'ui/MentionLabel': 'src/ui/MentionLabel/index.tsx',
113114
'ui/MessageContent': 'src/ui/MessageContent/index.tsx',
114115
'ui/MessageInput': 'src/ui/MessageInput/index.jsx',
115116
'ui/MessageItemMenu': 'src/ui/MessageItemMenu/index.tsx',

scripts/index_d_ts

+10
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,16 @@ declare module '@sendbird/uikit-react/ui/MentionUserLabel' {
14351435
export default MentionUserLabel;
14361436
}
14371437

1438+
declare module '@sendbird/uikit-react/ui/MentionLabel' {
1439+
interface MentionLabelProps {
1440+
mentionTemplate: string;
1441+
mentionedUserId: string;
1442+
isByMe: boolean;
1443+
}
1444+
const MentionUserLabel: React.FC<MentionLabelProps>;
1445+
export default MentionUserLabel;
1446+
}
1447+
14381448
declare module '@sendbird/uikit-react/ui/MessageContent' {
14391449
interface MessageContentProps {
14401450
className?: string | Array<string>;

src/styles/_color-themes.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ $themes: (
5656
shadow-gradient-5: var(--sendbird-light-shadow-05),
5757

5858
shadow-message-input: var(--sendbird-light-shadow-message-input),
59-
59+
highlight--1: var(--sendbird-highlight-100),
6060
on-light-4: var(--sendbird-light-onlight-04),
6161

6262
primary--1-4: var(--sendbird-light-primary-100),
@@ -134,6 +134,7 @@ $themes: (
134134
shadow-gradient-5: var(--sendbird-dark-shadow-05),
135135

136136
shadow-message-input: var(--sendbird-dark-shadow-message-input),
137+
highlight--1: var(--sendbird-highlight-100),
137138

138139
on-light-4: var(--sendbird-dark-onlight-04),
139140

src/styles/_dark-theme.scss

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
--sendbird-dark-secondary-100: #a8e2ab;
1515

1616
--sendbird-dark-information-100: #adc9ff;
17+
--sendbird-highlight-100: #fff2b6;
1718

1819
--sendbird-dark-error-500: #9d091e;
1920
--sendbird-dark-error-400: #bf0711;

src/styles/_light-theme.scss

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
--sendbird-light-secondary-100: #a8e2ab;
1515

1616
--sendbird-light-information-100: #adc9ff;
17+
--sendbird-highlight-100: #fff2b6;
1718

1819
--sendbird-light-error-500: #9d091e;
1920
--sendbird-light-error-400: #bf0711;

src/ui/MentionLabel/index.scss

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@import '../../styles/variables';
2+
3+
.sendbird-word {
4+
.sendbird-word__mention {
5+
&:hover {
6+
cursor: pointer;
7+
}
8+
display: inline-block;
9+
&.sendbird-word__mention--me {
10+
@include themed() {
11+
background-color: t(highlight--1);
12+
}
13+
.sendbird-label {
14+
@include themed() {
15+
color: t(on-information-1);
16+
}
17+
}
18+
}
19+
}
20+
}

src/ui/MentionLabel/index.tsx

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import './index.scss';
2+
3+
import React, {
4+
ReactElement,
5+
useRef,
6+
useState,
7+
useCallback,
8+
} from 'react';
9+
import type SendBird from 'sendbird';
10+
11+
import ContextMenu, { MenuItems } from '../ContextMenu';
12+
import Label, { LabelTypography, LabelColors } from '../Label';
13+
import UserProfile from '../UserProfile';
14+
import useSendbirdStateContext from '../../hooks/useSendbirdStateContext';
15+
16+
interface MentionLabelProps {
17+
mentionTemplate: string;
18+
mentionedUserId: string;
19+
isByMe: boolean;
20+
}
21+
22+
export default function MentionLabel(props: MentionLabelProps): JSX.Element {
23+
const {
24+
mentionTemplate,
25+
mentionedUserId,
26+
isByMe,
27+
} = props;
28+
29+
const mentionRef = useRef();
30+
31+
const sendbirdState = useSendbirdStateContext();
32+
const userId = sendbirdState?.config?.userId;
33+
const sdk = sendbirdState?.stores?.sdkStore?.sdk;
34+
const amIBeingMentioned = userId === mentionedUserId;
35+
const [user, setUser] = useState<SendBird.User| null>();
36+
const fetchUser = useCallback(
37+
(toggleDropdown) => {
38+
if (user) {
39+
toggleDropdown();
40+
}
41+
const query = sdk.createApplicationUserListQuery();
42+
query.userIdsFilter = [mentionedUserId];
43+
query.next((members) => {
44+
if (members?.length > 0) {
45+
setUser(members[0]);
46+
}
47+
toggleDropdown();
48+
})
49+
},
50+
[sdk, mentionedUserId],
51+
);
52+
return (
53+
<ContextMenu
54+
menuTrigger={(toggleDropdown: () => void): ReactElement => (
55+
<a
56+
className={`
57+
sendbird-word__mention
58+
${amIBeingMentioned ? 'sendbird-word__mention--me' : ''}
59+
`}
60+
onClick={() => fetchUser(toggleDropdown)}
61+
ref={mentionRef}
62+
>
63+
<Label
64+
type={LabelTypography.CAPTION_1}
65+
color={isByMe ? LabelColors.ONCONTENT_1 : LabelColors.ONBACKGROUND_1}
66+
>
67+
{`${mentionTemplate}${mentionedUserId}`}
68+
</Label>
69+
</a>
70+
)}
71+
menuItems={(closeDropdown: () => void): ReactElement => (
72+
<MenuItems
73+
/**
74+
* parentRef: For catching location(x, y) of MenuItems
75+
* parentContainRef: For toggling more options(menus & reactions)
76+
*/
77+
parentRef={mentionRef}
78+
parentContainRef={mentionRef}
79+
closeDropdown={closeDropdown}
80+
style={{ paddingTop: 0, paddingBottom: 0 }}
81+
>
82+
<UserProfile disableMessaging user={user} onSuccess={closeDropdown} />
83+
</MenuItems>
84+
)}
85+
/>
86+
)
87+
}

src/ui/UserProfile/index.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,26 @@ function UserProfile({
3939
<Avatar
4040
height="80px"
4141
width="80px"
42-
src={user.profileUrl}
42+
src={user?.profileUrl}
4343
/>
4444
</section>
4545
<section className="sendbird__user-profile-name">
4646
<Label
4747
type={LabelTypography.H_2}
4848
color={LabelColors.ONBACKGROUND_1}
4949
>
50-
{user.nickname || stringSet.NO_NAME}
50+
{user?.nickname || stringSet.NO_NAME}
5151
</Label>
5252
</section>
5353
{
54-
(user.userId !== currentUserId) && !disableMessaging && (
54+
(user?.userId !== currentUserId) && !disableMessaging && (
5555
<section className="sendbird__user-profile-message">
5656
<Button
5757
type={ButtonTypes.SECONDARY}
5858
onClick={() => {
5959
const params = new sdk.GroupChannelParams();
6060
params.isDistinct = true;
61-
params.addUserIds([user.userId]);
61+
params.addUserIds([user?.userId]);
6262
onSuccess();
6363
createChannel(params)
6464
.then((groupChannel) => {
@@ -85,7 +85,7 @@ function UserProfile({
8585
type={LabelTypography.BODY_1}
8686
color={LabelColors.ONBACKGROUND_1}
8787
>
88-
{user.userId}
88+
{user?.userId}
8989
</Label>
9090
</section>
9191
</div>

src/ui/Word/__tests__/__snapshots__/Word.spec.js.snap

+21-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,27 @@ exports[`Word should do a snapshot test of the Word DOM 1`] = `
55
className="sendbird-word"
66
>
77
hello
8-
<span
9-
className="sendbird-word__mention sendbird-label sendbird-label--caption-1 sendbird-label--color-onbackground-1"
8+
<div
9+
className="sendbird-context-menu"
10+
style={
11+
Object {
12+
"display": "inline",
13+
}
14+
}
1015
>
11-
@Hoon Baek
12-
</span>
16+
<a
17+
className="
18+
sendbird-word__mention
19+
20+
"
21+
onClick={[Function]}
22+
>
23+
<span
24+
className="sendbird-label sendbird-label--caption-1 sendbird-label--color-onbackground-1"
25+
>
26+
@Hoon Baek
27+
</span>
28+
</a>
29+
</div>
1330
</span>
1431
`;

src/ui/Word/index.scss

-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@
44
display: inline-block;
55
margin-right: 4px;
66
height: fit-content;
7-
.sendbird-word__mention {
8-
&:hover {
9-
cursor: pointer;
10-
}
11-
}
12-
.sendbird-word__mention,
137
.sendbird-word__url,
148
.sendbird-word__normal {
159
display: inline-block;

src/ui/Word/index.tsx

+7-8
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import React from 'react';
22
import SendBird from 'sendbird';
33
import './index.scss';
44

5-
import Label, { LabelTypography, LabelColors } from '../Label';
5+
import { LabelTypography, LabelColors } from '../Label';
66
import LinkLabel from '../LinkLabel';
77
import uuidv4 from '../../utils/uuid';
88
import { convertWordToStringObj, StringObjType, StringObj } from '../../utils';
9+
import MentionLabel from '../MentionLabel';
910

1011
interface WordProps {
1112
word: string;
@@ -34,14 +35,12 @@ export default function Word(props: WordProps): JSX.Element {
3435
}
3536
if (type === StringObjType.mention) {
3637
return (
37-
<Label
38+
<MentionLabel
39+
mentionTemplate={mentionTemplate}
40+
mentionedUserId={value}
3841
key={uuidv4()}
39-
className="sendbird-word__mention"
40-
type={LabelTypography.CAPTION_1}
41-
color={isByMe ? LabelColors.ONCONTENT_1 : LabelColors.ONBACKGROUND_1}
42-
>
43-
{`${mentionTemplate}${value}`}
44-
</Label>
42+
isByMe={isByMe}
43+
/>
4544
);
4645
} else if (type === StringObjType.url) {
4746
return (

0 commit comments

Comments
 (0)