Skip to content

Commit cc78d20

Browse files
committed
fix: sync ChannelPreview's lastMessage with latestMessagePreview
1 parent c852fe4 commit cc78d20

File tree

8 files changed

+119
-27
lines changed

8 files changed

+119
-27
lines changed

docusaurus/docs/React/components/utility-components/channel-preview-ui.mdx

+11-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ For even deeper customization of the channel list and channel previews, use the
1818
To customize the `ChannelList` item UI simply pass your custom `Preview` component to the `ChannelList`. See [The Preview Prop Component](../../guides/customization/channel-list-preview.mdx#the-preview-prop-component) for the extended guide.
1919

2020
```tsx
21-
const CustomChannelPreviewUI = ({ latestMessage, lastMessage }) => {
21+
const CustomChannelPreviewUI = ({ latestMessagePreview, lastMessage }) => {
2222
// "lastMessage" property is for the last
2323
// message that has been interacted with (pinned/edited/deleted)
2424

25-
// to display last message of the channel use "latestMessage" property
26-
return <span>{latestMessage}</span>;
25+
// to display last message of the channel use "latestMessagePreview" property
26+
return <span>{latestMessagePreview}</span>;
2727
};
2828

2929
<ChannelList Preview={CustomChannelPreviewUI} />;
@@ -105,6 +105,14 @@ The last message received in a channel.
105105

106106
### latestMessage
107107

108+
Deprecated, use `latestMessagePreview` instead.
109+
110+
| Type |
111+
| ----------------------- |
112+
| `string \| JSX.Element` |
113+
114+
### latestMessagePreview
115+
108116
Latest message preview to display. Will be either a string or a JSX.Element rendering markdown.
109117

110118
| Type |

docusaurus/docs/React/guides/customization/channel-list-preview.mdx

+6-6
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ Let's implement a simple custom preview:
5555
<TabItem value="js" label="React">
5656

5757
```jsx
58-
const CustomChannelPreview = ({ displayImage, displayTitle, latestMessage }) => (
58+
const CustomChannelPreview = ({ displayImage, displayTitle, latestMessagePreview }) => (
5959
<div className='channel-preview'>
6060
<img className='channel-preview__avatar' src={displayImage} alt='' />
6161
<div className='channel-preview__main'>
6262
<div className='channel-preview__header'>{displayTitle}</div>
63-
<div className='channel-preview__message'>{latestMessage}</div>
63+
<div className='channel-preview__message'>{latestMessagePreview}</div>
6464
</div>
6565
</div>
6666
);
@@ -122,7 +122,7 @@ message in the channel:
122122

123123
```jsx
124124
const CustomChannelPreview = (props) => {
125-
const { channel, displayImage, displayTitle, latestMessage } = props;
125+
const { channel, displayImage, displayTitle, latestMessagePreview } = props;
126126
const { userLanguage } = useTranslationContext();
127127
const latestMessageAt = channel.state.last_message_at;
128128

@@ -146,7 +146,7 @@ const CustomChannelPreview = (props) => {
146146
{timestamp}
147147
</time>
148148
</div>
149-
<div className='channel-preview__message'>{latestMessage}</div>
149+
<div className='channel-preview__message'>{latestMessagePreview}</div>
150150
</div>
151151
</div>
152152
);
@@ -217,7 +217,7 @@ const CustomChannelPreview = (props) => {
217217
activeChannel,
218218
displayImage,
219219
displayTitle,
220-
latestMessage,
220+
latestMessagePreview,
221221
setActiveChannel,
222222
} = props;
223223
const latestMessageAt = channel.state.last_message_at;
@@ -252,7 +252,7 @@ const CustomChannelPreview = (props) => {
252252
{timestamp}
253253
</time>
254254
</div>
255-
<div className='channel-preview__message'>{latestMessage}</div>
255+
<div className='channel-preview__message'>{latestMessagePreview}</div>
256256
</div>
257257
</button>
258258
);

src/components/ChannelPreview/ChannelPreview.tsx

+13-7
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ export type ChannelPreviewUIComponentProps<
2929
displayTitle?: string;
3030
/** The last message received in a channel */
3131
lastMessage?: StreamMessage<StreamChatGenerics>;
32-
/** Latest message preview to display, will be a string or JSX element supporting markdown. */
32+
/** @deprecated Use latestMessagePreview prop instead. */
3333
latestMessage?: string | JSX.Element;
34+
/** Latest message preview to display, will be a string or JSX element supporting markdown. */
35+
latestMessagePreview?: string | JSX.Element;
3436
/** Status describing whether own message has been delivered or read by another. If the last message is not an own message, then the status is undefined. */
3537
messageDeliveryStatus?: MessageDeliveryStatus;
3638
/** Number of unread Messages */
@@ -123,26 +125,29 @@ export const ChannelPreview = <
123125
useEffect(() => {
124126
refreshUnreadCount();
125127

126-
const handleEvent = (event: Event<StreamChatGenerics>) => {
127-
if (event.message) setLastMessage(event.message);
128+
const handleEvent = () => {
129+
setLastMessage(channel.state.messages[channel.state.messages.length - 1]);
128130
refreshUnreadCount();
129131
};
130132

131133
channel.on('message.new', handleEvent);
132134
channel.on('message.updated', handleEvent);
133135
channel.on('message.deleted', handleEvent);
136+
channel.on('message.undeleted', handleEvent);
137+
channel.on('channel.truncated', handleEvent);
134138

135139
return () => {
136140
channel.off('message.new', handleEvent);
137141
channel.off('message.updated', handleEvent);
138142
channel.off('message.deleted', handleEvent);
143+
channel.off('message.undeleted', handleEvent);
144+
channel.off('channel.truncated', handleEvent);
139145
};
140-
// eslint-disable-next-line react-hooks/exhaustive-deps
141-
}, [refreshUnreadCount, channelUpdateCount]);
146+
}, [channel, refreshUnreadCount, channelUpdateCount]);
142147

143148
if (!Preview) return null;
144149

145-
const latestMessage = getLatestMessagePreview(channel, t, userLanguage);
150+
const latestMessagePreview = getLatestMessagePreview(channel, t, userLanguage);
146151

147152
return (
148153
<Preview
@@ -151,7 +156,8 @@ export const ChannelPreview = <
151156
displayImage={displayImage}
152157
displayTitle={displayTitle}
153158
lastMessage={lastMessage}
154-
latestMessage={latestMessage}
159+
latestMessage={latestMessagePreview}
160+
latestMessagePreview={latestMessagePreview}
155161
messageDeliveryStatus={messageDeliveryStatus}
156162
setActiveChannel={setActiveChannel}
157163
unread={unread}

src/components/ChannelPreview/ChannelPreviewMessenger.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const UnMemoizedChannelPreviewMessenger = <
1818
className: customClassName = '',
1919
displayImage,
2020
displayTitle,
21-
latestMessage,
21+
latestMessagePreview,
2222
onSelect: customOnSelectChannel,
2323
setActiveChannel,
2424
unread,
@@ -70,7 +70,9 @@ const UnMemoizedChannelPreviewMessenger = <
7070
</div>
7171
)}
7272
</div>
73-
<div className='str-chat__channel-preview-messenger--last-message'>{latestMessage}</div>
73+
<div className='str-chat__channel-preview-messenger--last-message'>
74+
{latestMessagePreview}
75+
</div>
7476
</div>
7577
</button>
7678
);

src/components/ChannelPreview/__tests__/ChannelPreview.test.js

+73-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Chat } from '../../Chat';
88
import { ChatContext } from '../../../context/ChatContext';
99

1010
import {
11+
dispatchChannelTruncatedEvent,
1112
dispatchMessageDeletedEvent,
1213
dispatchMessageNewEvent,
1314
dispatchMessageUpdatedEvent,
@@ -24,11 +25,15 @@ import {
2425
useMockedApis,
2526
} from 'mock-builders';
2627

28+
const EMPTY_CHANNEL_PREVIEW_TEXT = 'Empty channel';
29+
2730
const PreviewUIComponent = (props) => (
2831
<>
2932
<div data-testid='channel-id'>{props.channel.id}</div>
3033
<div data-testid='unread-count'>{props.unread}</div>
31-
<div data-testid='last-event-message'>{props.lastMessage && props.lastMessage.text}</div>
34+
<div data-testid='last-event-message'>
35+
{props.lastMessage ? props.lastMessage.text : EMPTY_CHANNEL_PREVIEW_TEXT}
36+
</div>
3237
</>
3338
);
3439

@@ -65,7 +70,12 @@ describe('ChannelPreview', () => {
6570

6671
beforeEach(async () => {
6772
client = await getTestClientWithUser(user);
68-
useMockedApis(client, [queryChannelsApi([generateChannel(), generateChannel()])]);
73+
useMockedApis(client, [
74+
queryChannelsApi([
75+
generateChannel({ messages: Array.from({ length: 5 }, generateMessage) }),
76+
generateChannel({ messages: Array.from({ length: 5 }, generateMessage) }),
77+
]),
78+
]);
6979

7080
[c0, c1] = await client.queryChannels({}, {});
7181
});
@@ -132,10 +142,11 @@ describe('ChannelPreview', () => {
132142
['message.new', dispatchMessageNewEvent],
133143
['message.updated', dispatchMessageUpdatedEvent],
134144
['message.deleted', dispatchMessageDeletedEvent],
145+
['message.undeleted', dispatchMessageDeletedEvent],
135146
];
136147

137148
describe.each(eventCases)('On %s event', (eventType, dispatcher) => {
138-
it('should update lastMessage', async () => {
149+
it('should update latest message preview', async () => {
139150
const newUnreadCount = getRandomInt(1, 10);
140151
c0.countUnread = () => newUnreadCount;
141152

@@ -151,9 +162,10 @@ describe('ChannelPreview', () => {
151162
expect(getByTestId('channel-id')).toBeInTheDocument();
152163
});
153164

154-
const message = generateMessage();
155-
act(() => {
156-
dispatcher(client, message, c0);
165+
const message =
166+
eventType === 'message.new' ? generateMessage() : c0.state.messages.slice(-1)[0];
167+
await act(async () => {
168+
await dispatcher(client, message, c0);
157169
});
158170

159171
await expectLastEventMessageToBe(getByTestId, message.text);
@@ -233,6 +245,61 @@ describe('ChannelPreview', () => {
233245
});
234246
});
235247

248+
it('on channel.truncated event should update latest message preview', async () => {
249+
const newUnreadCount = getRandomInt(1, 10);
250+
c0.countUnread = () => newUnreadCount;
251+
252+
const { getByTestId } = renderComponent(
253+
{
254+
activeChannel: c1,
255+
channel: c0,
256+
},
257+
render,
258+
);
259+
260+
await waitFor(() => {
261+
expect(getByTestId('channel-id')).toBeInTheDocument();
262+
});
263+
264+
await act(async () => {
265+
await dispatchChannelTruncatedEvent(client, c0);
266+
});
267+
268+
await expectLastEventMessageToBe(getByTestId, EMPTY_CHANNEL_PREVIEW_TEXT);
269+
});
270+
271+
it.each([
272+
['message.updated', dispatchMessageUpdatedEvent],
273+
['message.deleted', dispatchMessageDeletedEvent],
274+
['message.undeleted', dispatchMessageDeletedEvent],
275+
])(
276+
'on %s event should not update latest message preview for the non-last message',
277+
async (_, dispatcher) => {
278+
const newUnreadCount = getRandomInt(1, 10);
279+
c0.countUnread = () => newUnreadCount;
280+
281+
const { getByTestId } = renderComponent(
282+
{
283+
activeChannel: c1,
284+
channel: c0,
285+
},
286+
render,
287+
);
288+
289+
await waitFor(() => {
290+
expect(getByTestId('channel-id')).toBeInTheDocument();
291+
});
292+
293+
const lastMessage = c0.state.messages.slice(-1)[0];
294+
const penultimateMessage = c0.state.messages.slice(-2)[0];
295+
await act(async () => {
296+
await dispatcher(client, penultimateMessage, c0);
297+
});
298+
299+
await expectLastEventMessageToBe(getByTestId, lastMessage.text);
300+
},
301+
);
302+
236303
describe('notification.mark_read', () => {
237304
it('should set unread count to 0 for event missing CID', () => {
238305
const unreadCount = getRandomInt(1, 10);

src/components/ChannelPreview/__tests__/ChannelPreviewMessenger.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('ChannelPreviewMessenger', () => {
3030
channel={channel}
3131
displayImage='https://randomimage.com/src.jpg'
3232
displayTitle='Channel name'
33-
latestMessage='Latest message!'
33+
latestMessagePreview='Latest message!'
3434
setActiveChannel={jest.fn()}
3535
unread={10}
3636
{...props}

src/components/ChannelPreview/utils.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ export const getLatestMessagePreview = <
3232
}
3333

3434
if (previewTextToRender) {
35-
const renderedText = renderPreviewText(previewTextToRender);
36-
return renderedText;
35+
return renderPreviewText(previewTextToRender);
3736
}
3837

3938
if (latestMessage.command) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export default (client, message, channel = {}) => {
2+
const [channel_id, channel_type] = channel.cid.split(':');
3+
client.dispatchEvent({
4+
channel_id,
5+
channel_type,
6+
cid: channel.cid,
7+
message,
8+
type: 'message.undeleted',
9+
});
10+
};

0 commit comments

Comments
 (0)