Skip to content

Commit 4278599

Browse files
committed
feat: reflect user.messages.deleted WS event
1 parent f5319f6 commit 4278599

File tree

6 files changed

+186
-11
lines changed

6 files changed

+186
-11
lines changed

src/components/Channel/Channel.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,11 @@ const ChannelInner = (
425425
});
426426
}
427427

428+
// ignore the event if it is not targeted at the current channel.
429+
// Event targeted at this channel or globally targeted event should lead to state refresh
430+
if (event.type === 'user.messages.deleted' && event.cid && event.cid !== channel.cid)
431+
return;
432+
428433
if (event.type === 'user.watching.start' || event.type === 'user.watching.stop')
429434
return;
430435

@@ -568,6 +573,7 @@ const ChannelInner = (
568573
client.on('connection.recovered', handleEvent);
569574
client.on('user.updated', handleEvent);
570575
client.on('user.deleted', handleEvent);
576+
client.on('user.messages.deleted', handleEvent);
571577
channel.on(handleEvent);
572578
}
573579
})();

src/components/ChannelPreview/ChannelPreview.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,12 @@ export const ChannelPreview = (props: ChannelPreviewProps) => {
135135
useEffect(() => {
136136
refreshUnreadCount();
137137

138-
const handleEvent = () => {
138+
const handleEvent = (event: Event) => {
139+
const deletedMessagesInAnotherChannel =
140+
event.type === 'user.messages.deleted' && event.cid && event.cid !== channel.cid;
141+
142+
if (deletedMessagesInAnotherChannel) return;
143+
139144
setLastMessage(
140145
channel.state.latestMessages[channel.state.latestMessages.length - 1],
141146
);
@@ -145,17 +150,19 @@ export const ChannelPreview = (props: ChannelPreviewProps) => {
145150
channel.on('message.new', handleEvent);
146151
channel.on('message.updated', handleEvent);
147152
channel.on('message.deleted', handleEvent);
153+
client.on('user.messages.deleted', handleEvent);
148154
channel.on('message.undeleted', handleEvent);
149155
channel.on('channel.truncated', handleEvent);
150156

151157
return () => {
152158
channel.off('message.new', handleEvent);
153159
channel.off('message.updated', handleEvent);
154160
channel.off('message.deleted', handleEvent);
161+
client.off('user.messages.deleted', handleEvent);
155162
channel.off('message.undeleted', handleEvent);
156163
channel.off('channel.truncated', handleEvent);
157164
};
158-
}, [channel, refreshUnreadCount, channelUpdateCount]);
165+
}, [channel, client, refreshUnreadCount, channelUpdateCount]);
159166

160167
if (!Preview) return null;
161168

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

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
dispatchMessageUpdatedEvent,
1616
dispatchNotificationMarkRead,
1717
dispatchNotificationMarkUnread,
18+
dispatchUserMessagesDeletedEvent,
1819
dispatchUserUpdatedEvent,
1920
generateChannel,
2021
generateMember,
@@ -363,6 +364,131 @@ describe('ChannelPreview', () => {
363364
},
364365
);
365366

367+
describe('user.messages.deleted', () => {
368+
const deletedMessageText = 'Message deleted';
369+
const user = { id: 'banned-user' };
370+
371+
it('should update latest message preview for all ChannelPreviews if global ban', async () => {
372+
const {
373+
channels: [c0, c1],
374+
client,
375+
} = await initClientWithChannels({
376+
channelsData: [
377+
generateChannel({
378+
messages: [
379+
generateMessage({
380+
created_at: '1970-01-01T00:00:00.000Z',
381+
user: { id: 'other-user' },
382+
}),
383+
generateMessage({ created_at: '1970-01-02T00:00:00.000Z', user }),
384+
],
385+
}),
386+
generateChannel({
387+
messages: [
388+
generateMessage({
389+
created_at: '1971-01-01T00:00:00.000Z',
390+
user: { id: 'other-user' },
391+
}),
392+
generateMessage({ created_at: '1971-01-02T00:00:00.000Z', user }),
393+
],
394+
}),
395+
],
396+
customUser: user,
397+
});
398+
399+
const { container } = render(
400+
<ChatContext.Provider
401+
value={{
402+
channel: c0,
403+
client,
404+
setActiveChannel: () => jest.fn(),
405+
}}
406+
>
407+
<ChannelPreview channel={c0} />
408+
<ChannelPreview channel={c1} />
409+
</ChatContext.Provider>,
410+
);
411+
412+
await act(() => {
413+
dispatchUserMessagesDeletedEvent({
414+
client,
415+
user,
416+
});
417+
});
418+
419+
await waitFor(() => {
420+
const lastMessagePreviews = container.querySelectorAll(
421+
'.str-chat__channel-preview-messenger--last-message',
422+
);
423+
expect(lastMessagePreviews.length).toBe(2);
424+
lastMessagePreviews.forEach((preview) => {
425+
expect(preview).toHaveTextContent(deletedMessageText);
426+
});
427+
});
428+
});
429+
430+
it('should update latest message preview if the channel is the target', async () => {
431+
const {
432+
channels: [c0, c1],
433+
client,
434+
} = await initClientWithChannels({
435+
channelsData: [
436+
generateChannel({
437+
messages: [
438+
generateMessage({
439+
created_at: '1970-01-01T00:00:00.000Z',
440+
user: { id: 'other-user' },
441+
}),
442+
generateMessage({ created_at: '1970-01-02T00:00:00.000Z', user }),
443+
],
444+
}),
445+
generateChannel({
446+
messages: [
447+
generateMessage({
448+
created_at: '1971-01-01T00:00:00.000Z',
449+
user: { id: 'other-user' },
450+
}),
451+
generateMessage({ created_at: '1971-01-02T00:00:00.000Z', user }),
452+
],
453+
}),
454+
],
455+
customUser: user,
456+
});
457+
458+
const { container } = render(
459+
<ChatContext.Provider
460+
value={{
461+
channel: c0,
462+
client,
463+
setActiveChannel: () => jest.fn(),
464+
}}
465+
>
466+
<ChannelPreview channel={c0} />
467+
<ChannelPreview channel={c1} />
468+
</ChatContext.Provider>,
469+
);
470+
471+
await act(() => {
472+
dispatchUserMessagesDeletedEvent({
473+
channel: c0, // target
474+
client,
475+
user,
476+
});
477+
});
478+
479+
await waitFor(() => {
480+
const lastMessagePreviews = container.querySelectorAll(
481+
'.str-chat__channel-preview-messenger--last-message',
482+
);
483+
expect(lastMessagePreviews.length).toBe(2);
484+
expect(lastMessagePreviews[0]).toHaveTextContent(deletedMessageText);
485+
expect(lastMessagePreviews[1]).toHaveTextContent(
486+
c1.state.messages.slice(-1)[0].text,
487+
);
488+
});
489+
});
490+
});
491+
366492
describe('notification.mark_read', () => {
367493
it('should set unread count to 0 for event missing CID', async () => {
368494
const unreadCount = getRandomInt(1, 10);

src/mock-builders/event/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ export { default as dispatchNotificationMarkUnread } from './notificationMarkUnr
1515
export { default as dispatchNotificationMessageNewEvent } from './notificationMessageNew';
1616
export { default as dispatchNotificationMutesUpdated } from './notificationMutesUpdated';
1717
export { default as dispatchNotificationRemovedFromChannel } from './notificationRemovedFromChannel';
18+
export { default as dispatchUserMessagesDeletedEvent } from './userMessagesDeleted';
1819
export { default as dispatchUserUpdatedEvent } from './userUpdated';
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { ChannelResponse, StreamChat, UserResponse } from 'stream-chat';
2+
3+
export default ({
4+
channel,
5+
client,
6+
hardDelete,
7+
user,
8+
}: {
9+
channel: ChannelResponse & { name?: string }; // mock-builders are excluded in tsconfig.json
10+
client: StreamChat;
11+
hardDelete?: boolean;
12+
user: UserResponse;
13+
}) => {
14+
if (channel) {
15+
const [channel_id, channel_type] = channel.cid.split(':');
16+
client.dispatchEvent({
17+
channel_custom: {
18+
// archived: false,
19+
name: channel.name,
20+
},
21+
channel_id,
22+
channel_member_count: 2,
23+
channel_type,
24+
cid: channel.cid,
25+
created_at: new Date().toISOString(),
26+
hard_delete: !!hardDelete,
27+
type: 'user.messages.deleted',
28+
user,
29+
});
30+
} else {
31+
client.dispatchEvent({
32+
created_at: new Date().toISOString(),
33+
hard_delete: !!hardDelete,
34+
type: 'user.messages.deleted',
35+
user,
36+
});
37+
}
38+
};

src/mock-builders/generator/channel.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import { nanoid } from 'nanoid';
2-
import type { ChannelConfigWithInfo, ChannelResponse } from 'stream-chat';
2+
import type { ChannelAPIResponse, ChannelConfigWithInfo } from 'stream-chat';
3+
import type { DeepPartial } from '../../../../stream-chat-js/src/types.utility';
34

4-
export const generateChannel = (
5-
options: {
6-
channel?: Partial<ChannelResponse>;
7-
config?: Partial<ChannelConfigWithInfo>;
8-
} = { channel: {}, config: {} },
9-
) => {
10-
const { channel: optionsChannel, ...optionsBesidesChannel } = options;
5+
export const generateChannel = (options?: DeepPartial<ChannelAPIResponse>) => {
6+
const { channel: optionsChannel, ...optionsBesidesChannel } =
7+
options ?? ({} as ChannelAPIResponse);
118
const id = optionsChannel?.id ?? nanoid();
129
const type = optionsChannel?.type ?? 'messaging';
1310
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -68,6 +65,6 @@ export const generateChannel = (
6865
type,
6966
updated_at: '2020-04-28T11:20:48.578147Z',
7067
...restOptionsChannel,
71-
} as ChannelResponse,
68+
},
7269
};
7370
};

0 commit comments

Comments
 (0)