Skip to content

Commit 35d4d20

Browse files
authored
fix: Fixed markdown message in channel preview (#1187)
Fixes [CLNP-4063](https://sendbird.atlassian.net/browse/CLNP-4063) ### Changelogs - Fixed a bug where markdown messages are incorrectly displayed in channel preview <img width="695" alt="Screenshot 2024-08-05 at 12 34 49 PM" src="https://github.com/user-attachments/assets/6273e660-145e-424f-932a-292079e74588"> ### Disabled <img width="1008" alt="Screenshot 2024-08-01 at 3 16 11 PM" src="https://github.com/user-attachments/assets/360376e9-bc70-4fba-ab47-2fa9699bcb55"> <img width="449" alt="Screenshot 2024-08-01 at 3 19 25 PM" src="https://github.com/user-attachments/assets/3614b659-59d2-4c33-ab50-87d57f5f7c43"> ### Enabled <img width="1014" alt="Screenshot 2024-08-01 at 3 10 04 PM" src="https://github.com/user-attachments/assets/3c2e0f23-7dde-4a62-9c69-07225f9cc890"> <img width="434" alt="Screenshot 2024-08-01 at 3 19 33 PM" src="https://github.com/user-attachments/assets/f990e6ee-f7cf-4202-9d92-41f3d1561cf7"> [CLNP-4063]: https://sendbird.atlassian.net/browse/CLNP-4063?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent aa43bdb commit 35d4d20

File tree

6 files changed

+119
-14
lines changed

6 files changed

+119
-14
lines changed

src/modules/GroupChannelList/components/GroupChannelListItem/GroupChannelListItemView.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useLocalization } from '../../../../lib/LocalizationContext';
1111
import { useMediaQueryContext } from '../../../../lib/MediaQueryContext';
1212
import { noop } from '../../../../utils/utils';
1313
import { CoreMessageType, isVoiceMessage } from '../../../../utils';
14-
import { getChannelUnreadMessageCount, getLastMessage, getLastMessageCreatedAt, getTotalMembers } from './utils';
14+
import { getChannelUnreadMessageCount, getLastMessageText, getLastMessageCreatedAt, getTotalMembers } from './utils';
1515

1616
import { TypingIndicatorText } from '../../../GroupChannel/components/TypingIndicator';
1717
import { GroupChannelPreviewActionProps } from '../GroupChannelPreviewAction';
@@ -24,6 +24,7 @@ import MentionUserLabel from '../../../../ui/MentionUserLabel';
2424
import MessageStatus from '../../../../ui/MessageStatus';
2525
import Modal from '../../../../ui/Modal';
2626
import TextButton from '../../../../ui/TextButton';
27+
import { getChannelPreviewMessage } from '../../../Message/utils/tokens/tokenize';
2728

2829
export interface GroupChannelListItemBasicProps {
2930
tabIndex: number;
@@ -56,6 +57,10 @@ export const GroupChannelListItemView = ({
5657
const { dateLocale, stringSet } = useLocalization();
5758
const { isMobile } = useMediaQueryContext();
5859
const isMentionEnabled = config.groupChannel.enableMention;
60+
const lastMessage = getLastMessageText(channel, stringSet);
61+
const previewLastMessage = config.groupChannel.enableMarkdownForUserMessage
62+
? getChannelPreviewMessage(lastMessage)
63+
: lastMessage;
5964

6065
const [showMobileLeave, setShowMobileLeave] = useState(false);
6166
const onLongPress = useLongPress(
@@ -161,7 +166,7 @@ export const GroupChannelListItemView = ({
161166
)}
162167
{!isTyping
163168
&& !isVoiceMessage(channel.lastMessage as FileMessage | null)
164-
&& getLastMessage(channel, stringSet)}
169+
&& previewLastMessage}
165170
{!isTyping
166171
&& isVoiceMessage(channel.lastMessage as FileMessage | null)
167172
&& stringSet.VOICE_MESSAGE}

src/modules/GroupChannelList/components/GroupChannelListItem/__tests__/GroupChannelListItem.spec.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
getLastMessage,
2+
getLastMessageText,
33
getChannelTitle,
44
getTotalMembers,
55
getChannelUnreadMessageCount,
@@ -86,28 +86,28 @@ describe('GroupChannelListItem', () => {
8686
}
8787
};
8888
expect(
89-
getLastMessage(channel, LabelStringSet)
89+
getLastMessageText(channel, LabelStringSet)
9090
).toBe('');
9191
expect(
92-
getLastMessage(channel2, LabelStringSet)
92+
getLastMessageText(channel2, LabelStringSet)
9393
).toBe('');
9494
expect(
95-
getLastMessage(channel3, LabelStringSet)
95+
getLastMessageText(channel3, LabelStringSet)
9696
).toBe(text);
9797
expect(
98-
getLastMessage(channel4, LabelStringSet)
98+
getLastMessageText(channel4, LabelStringSet)
9999
).toBe(LabelStringSet.CHANNEL_PREVIEW_LAST_MESSAGE_FILE_TYPE_GENERAL);
100100
expect(
101-
getLastMessage(channel5, LabelStringSet)
101+
getLastMessageText(channel5, LabelStringSet)
102102
).toBe(LabelStringSet.CHANNEL_PREVIEW_LAST_MESSAGE_FILE_TYPE_PHOTO);
103103
expect(
104-
getLastMessage(channel6, LabelStringSet)
104+
getLastMessageText(channel6, LabelStringSet)
105105
).toBe(LabelStringSet.CHANNEL_PREVIEW_LAST_MESSAGE_FILE_TYPE_GIF);
106106
expect(
107-
getLastMessage(mfmGifChannel, LabelStringSet)
107+
getLastMessageText(mfmGifChannel, LabelStringSet)
108108
).toBe(LabelStringSet.CHANNEL_PREVIEW_LAST_MESSAGE_FILE_TYPE_PHOTO);
109109
expect(
110-
getLastMessage(mfmPhotoChannel, LabelStringSet)
110+
getLastMessageText(mfmPhotoChannel, LabelStringSet)
111111
).toBe(LabelStringSet.CHANNEL_PREVIEW_LAST_MESSAGE_FILE_TYPE_PHOTO);
112112
});
113113

src/modules/GroupChannelList/components/GroupChannelListItem/index.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@
122122

123123
.sendbird-channel-preview__content__lower__last-message {
124124
overflow: hidden;
125-
text-overflow: ellipsis;
125+
display: -webkit-box;
126+
-webkit-line-clamp: 2;
127+
-webkit-box-orient: vertical;
126128
}
127129

128130
.sendbird-channel-preview__content__lower__unread-message-count {

src/modules/GroupChannelList/components/GroupChannelListItem/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ const getPrettyLastMessage = (message = null, stringSet = LabelStringSet) => {
7979
return message.message ?? '';
8080
};
8181

82-
export const getLastMessage = (channel?: GroupChannel, stringSet = LabelStringSet) => channel?.lastMessage ? getPrettyLastMessage(channel?.lastMessage, stringSet) : '';
82+
export const getLastMessageText = (channel?: GroupChannel, stringSet = LabelStringSet) => {
83+
return channel?.lastMessage ? getPrettyLastMessage(channel?.lastMessage, stringSet) : '';
84+
};
8385

8486
export const getChannelUnreadMessageCount = (channel?: GroupChannel) => channel?.unreadMessageCount ? channel.unreadMessageCount : 0;

src/modules/Message/utils/tokens/__tests__/tokenizeUtils.spec.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,85 @@ import {
55
identifyUrlsAndStrings,
66
combineNearbyStrings,
77
getWhiteSpacePreservedText,
8+
getChannelPreviewMessage,
9+
markDownTokenResolver,
810
} from '../tokenize';
9-
import { Token, UndeterminedToken } from '../types';
11+
import { MarkdownToken, Token, UndeterminedToken } from '../types';
12+
13+
describe('markDownTokenResolver', () => {
14+
it('when given tokens without markdown type, return original text without change.', () => {
15+
const text = 'This is a channel\'s last message.';
16+
const tokens = [
17+
{ type: 'undetermined', value: 'This is a channel\'s last message.' } as UndeterminedToken,
18+
];
19+
const result = markDownTokenResolver(tokens);
20+
expect(result).toEqual(text);
21+
});
22+
23+
it('when given a tokens with markdown type, return transformed text (#1).', () => {
24+
const text = 'This is a channel\'s last message with a bold url.';
25+
const tokens = [
26+
{
27+
type: 'undetermined',
28+
value: 'This is a channel\'s last message with a ',
29+
} as UndeterminedToken,
30+
{
31+
type: 'markdown',
32+
markdownType: 'url',
33+
value: '[**bold url**](www.google.com)',
34+
groups: [
35+
'[**bold url**](www.google.com)',
36+
'**bold url**',
37+
'www.google.com',
38+
],
39+
} as MarkdownToken,
40+
{ type: 'undetermined', value: '.' } as UndeterminedToken,
41+
];
42+
const result = markDownTokenResolver(tokens);
43+
expect(result).toEqual(text);
44+
});
45+
46+
it('when given a tokens with markdown type, return transformed text (#2).', () => {
47+
const text = 'This is a channel\'s last message with a bold url.';
48+
const tokens = [
49+
{
50+
type: 'undetermined',
51+
value: 'This is a channel\'s last message with a ',
52+
} as UndeterminedToken,
53+
{
54+
type: 'markdown',
55+
markdownType: 'bold',
56+
value: '**[bold url](www.google.com)**',
57+
groups: ['**[bold url](www.google.com)**', '[bold url](www.google.com)'],
58+
} as MarkdownToken,
59+
{ type: 'undetermined', value: '.' } as UndeterminedToken,
60+
];
61+
const result = markDownTokenResolver(tokens);
62+
expect(result).toEqual(text);
63+
});
64+
});
65+
66+
describe('getChannelPreviewMessage', () => {
67+
it('when given normal text, return original text without change.', () => {
68+
const text = 'This is a channel\'s last message.';
69+
const result = getChannelPreviewMessage(text);
70+
expect(result).toEqual(text);
71+
});
72+
73+
it('when given a text with markdown syntaxes, return transformed text (#1).', () => {
74+
const text = 'This is a channel\'s last message with a [**bold url**](www.google.com).';
75+
const expected = 'This is a channel\'s last message with a bold url.';
76+
const result = getChannelPreviewMessage(text);
77+
expect(result).toEqual(expected);
78+
});
79+
80+
it('when given a text with markdown syntaxes, return transformed text (#2).', () => {
81+
const text = 'This is a channel\'s last message with a **[bold url](www.google.com)**.';
82+
const expected = 'This is a channel\'s last message with a bold url.';
83+
const result = getChannelPreviewMessage(text);
84+
expect(result).toEqual(expected);
85+
});
86+
});
1087

1188
describe('getUserMentionRegex', () => {
1289
it('should return a regex with the correct pattern', () => {

src/modules/Message/utils/tokens/tokenize.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,25 @@ export function tokenizeMessage({
202202
return result;
203203
}
204204

205+
export function getChannelPreviewMessage(messageText: string): string {
206+
const partialResult = [{
207+
type: TOKEN_TYPES.undetermined,
208+
value: messageText,
209+
}];
210+
const tokens = splitTokensWithMarkdowns(partialResult);
211+
return markDownTokenResolver(tokens);
212+
}
213+
214+
export function markDownTokenResolver(tokens: Token[]): string {
215+
const result = tokens.map((token) => {
216+
if (token.type === TOKEN_TYPES.markdown) {
217+
return markDownTokenResolver(tokenizeMarkdown({ messageText: (token as MarkdownToken).groups[1] }));
218+
}
219+
return token.value;
220+
});
221+
return result.join('');
222+
}
223+
205224
export function tokenizeMarkdown({
206225
messageText,
207226
}): Token[] {

0 commit comments

Comments
 (0)