Skip to content

Commit b818b0f

Browse files
committed
fix: channel hook regressions
1 parent 32115ca commit b818b0f

File tree

3 files changed

+78
-33
lines changed

3 files changed

+78
-33
lines changed

package/src/components/ChannelPreview/ChannelPreview.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import React from 'react';
33
import type { Channel } from 'stream-chat';
44

55
import { useChannelPreviewData } from './hooks/useChannelPreviewData';
6-
import { useLatestMessagePreview } from './hooks/useLatestMessagePreview';
76

87
import {
98
ChannelsContextValue,
@@ -31,15 +30,16 @@ export const ChannelPreview = <
3130
const { channel, client: propClient, forceUpdate: propForceUpdate, Preview: propPreview } = props;
3231

3332
const { client: contextClient } = useChatContext<StreamChatGenerics>();
34-
const { forceUpdate: contextForceUpdate, Preview: contextPreview } =
35-
useChannelsContext<StreamChatGenerics>();
33+
const { Preview: contextPreview } = useChannelsContext<StreamChatGenerics>();
3634

3735
const client = propClient || contextClient;
38-
const forceUpdate = propForceUpdate || contextForceUpdate;
3936
const Preview = propPreview || contextPreview;
4037

41-
const { lastMessage, muted, unread } = useChannelPreviewData(channel, client, forceUpdate);
42-
const latestMessagePreview = useLatestMessagePreview(channel, forceUpdate, lastMessage);
38+
const { latestMessagePreview, muted, unread } = useChannelPreviewData(
39+
channel,
40+
client,
41+
propForceUpdate,
42+
);
4343

4444
return (
4545
<Preview

package/src/components/ChannelPreview/__tests__/ChannelPreview.test.tsx

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ describe('ChannelPreview', () => {
6969
);
7070
};
7171

72+
const generateChannelWrapper = (overrides: Record<string, unknown>) =>
73+
generateChannel({
74+
countUnread: jest.fn().mockReturnValue(0),
75+
initialized: true,
76+
lastMessage: jest.fn().mockReturnValue(generateMessage()),
77+
muteStatus: jest.fn().mockReturnValue({ muted: false }),
78+
...overrides,
79+
});
80+
7281
const useInitializeChannel = async (c: GetOrCreateChannelApiParams) => {
7382
useMockedApis(chatClient, [getOrCreateChannelApi(c)]);
7483

@@ -89,9 +98,8 @@ describe('ChannelPreview', () => {
8998
it("should not update the unread count if the event's cid does not match the channel's cid", async () => {
9099
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });
91100

92-
const c = generateChannel({
101+
const c = generateChannelWrapper({
93102
countUnread: jest.fn().mockReturnValue(10),
94-
muteStatus: jest.fn().mockReturnValue({ muted: false }),
95103
on: channelOnMock,
96104
});
97105

@@ -117,9 +125,8 @@ describe('ChannelPreview', () => {
117125
it('should update the unread count to 0', async () => {
118126
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });
119127

120-
const c = generateChannel({
128+
const c = generateChannelWrapper({
121129
countUnread: jest.fn().mockReturnValue(10),
122-
muteStatus: jest.fn().mockReturnValue({ muted: false }),
123130
on: channelOnMock,
124131
});
125132

@@ -147,9 +154,7 @@ describe('ChannelPreview', () => {
147154
it("should not update the unread count if the event's cid is undefined", async () => {
148155
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });
149156

150-
const c = generateChannel({
151-
countUnread: jest.fn().mockReturnValue(0),
152-
muteStatus: jest.fn().mockReturnValue({ muted: false }),
157+
const c = generateChannelWrapper({
153158
on: channelOnMock,
154159
});
155160

@@ -182,9 +187,7 @@ describe('ChannelPreview', () => {
182187
it("should not update the unread count if the event's cid does not match the channel's cid", async () => {
183188
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });
184189

185-
const c = generateChannel({
186-
countUnread: jest.fn().mockReturnValue(0),
187-
muteStatus: jest.fn().mockReturnValue({ muted: false }),
190+
const c = generateChannelWrapper({
188191
on: channelOnMock,
189192
});
190193

@@ -217,9 +220,7 @@ describe('ChannelPreview', () => {
217220
it("should not update the unread count if the event's user id does not match the client's user id", async () => {
218221
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });
219222

220-
const c = generateChannel({
221-
countUnread: jest.fn().mockReturnValue(0),
222-
muteStatus: jest.fn().mockReturnValue({ muted: false }),
223+
const c = generateChannelWrapper({
223224
on: channelOnMock,
224225
});
225226

@@ -255,12 +256,10 @@ describe('ChannelPreview', () => {
255256
await useInitializeChannel(c);
256257
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });
257258

258-
const testChannel = {
259+
const testChannel = generateChannelWrapper({
259260
...channel,
260-
countUnread: jest.fn().mockReturnValue(0),
261-
muteStatus: jest.fn().mockReturnValue({ muted: false }),
262261
on: channelOnMock,
263-
};
262+
});
264263

265264
const { getByTestId } = render(<TestComponent />);
266265

@@ -291,9 +290,9 @@ describe('ChannelPreview', () => {
291290
it('should update the unread count to 0 if the channel is muted', async () => {
292291
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });
293292

294-
const c = generateChannel({
293+
const c = generateChannelWrapper({
295294
countUnread: jest.fn().mockReturnValue(10),
296-
muteStatus: jest.fn().mockReturnValue({ muted: false }),
295+
muteStatus: jest.fn().mockReturnValue({ muted: true }),
297296
on: channelOnMock,
298297
});
299298

@@ -304,7 +303,7 @@ describe('ChannelPreview', () => {
304303
await waitFor(() => getByTestId('channel-id'));
305304

306305
await waitFor(() => {
307-
expect(getByTestId('unread-count')).toHaveTextContent('10');
306+
expect(getByTestId('unread-count')).toHaveTextContent('0');
308307
});
309308

310309
act(() => {

package/src/components/ChannelPreview/hooks/useChannelPreviewData.ts

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,56 @@ import type { Channel, ChannelState, Event, MessageResponse, StreamChat } from '
55

66
import { useIsChannelMuted } from './useIsChannelMuted';
77

8+
import { useLatestMessagePreview } from './useLatestMessagePreview';
9+
10+
import { useChannelsContext } from '../../../contexts';
811
import type { DefaultStreamChatGenerics } from '../../../types/types';
912

1013
export const useChannelPreviewData = <
1114
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
1215
>(
1316
channel: Channel<StreamChatGenerics>,
1417
client: StreamChat<StreamChatGenerics>,
15-
forceUpdate?: number,
18+
forceUpdateOverride?: number,
1619
) => {
20+
const [forceUpdate, setForceUpdate] = useState(0);
1721
const [lastMessage, setLastMessage] = useState<
1822
| ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>
1923
| MessageResponse<StreamChatGenerics>
2024
>(channel.state.messages[channel.state.messages.length - 1]);
2125
const [unread, setUnread] = useState(channel.countUnread());
2226
const { muted } = useIsChannelMuted(channel);
27+
const { forceUpdate: contextForceUpdate } = useChannelsContext<StreamChatGenerics>();
28+
const channelListForceUpdate = forceUpdateOverride ?? contextForceUpdate;
29+
30+
const channelLastMessage = channel.lastMessage();
31+
const channelLastMessageString = `${channelLastMessage?.id}${channelLastMessage?.updated_at}`;
32+
33+
useEffect(() => {
34+
if (
35+
channelLastMessage &&
36+
(channelLastMessage.id !== lastMessage?.id ||
37+
channelLastMessage.updated_at !== lastMessage?.updated_at)
38+
) {
39+
setLastMessage(channelLastMessage);
40+
}
41+
const newUnreadCount = channel.countUnread();
42+
setUnread(newUnreadCount);
43+
// eslint-disable-next-line react-hooks/exhaustive-deps
44+
}, [channel, channelLastMessageString, channelListForceUpdate]);
2345

2446
/**
2547
* This effect listens for the `notification.mark_read` event and sets the unread count to 0
2648
*/
2749
useEffect(() => {
2850
const handleReadEvent = (event: Event) => {
2951
if (!event.cid) return;
30-
if (channel.cid === event.cid) setUnread(0);
52+
if (channel.cid !== event.cid) return;
53+
if (event?.user?.id === client.userID) {
54+
setUnread(0);
55+
} else if (event?.user?.id) {
56+
setForceUpdate((prev) => prev + 1);
57+
}
3158
};
3259
const { unsubscribe } = client.on('notification.mark_read', handleReadEvent);
3360
return unsubscribe;
@@ -70,16 +97,35 @@ export const useChannelPreviewData = <
7097
refreshUnreadCount();
7198
};
7299

100+
const handleNewMessageEvent = (event: Event<StreamChatGenerics>) => {
101+
const message = event.message;
102+
if (message && (!message.parent_id || message.show_in_channel)) {
103+
setLastMessage(message);
104+
setUnread(channel.countUnread());
105+
}
106+
};
107+
108+
const handleUpdatedOrDeletedMessage = (event: Event<StreamChatGenerics>) => {
109+
setLastMessage((prevLastMessage) => {
110+
if (prevLastMessage?.id === event.message?.id) {
111+
return event.message;
112+
}
113+
return prevLastMessage;
114+
});
115+
};
116+
73117
const listeners = [
74-
channel.on('message.new', handleEvent),
75-
channel.on('message.updated', handleEvent),
76-
channel.on('message.deleted', handleEvent),
118+
channel.on('message.new', handleNewMessageEvent),
119+
channel.on('message.updated', handleUpdatedOrDeletedMessage),
120+
channel.on('message.deleted', handleUpdatedOrDeletedMessage),
77121
channel.on('message.undeleted', handleEvent),
78122
channel.on('channel.truncated', handleEvent),
79123
];
80124

81125
return () => listeners.forEach((l) => l.unsubscribe());
82-
}, [channel, refreshUnreadCount, forceUpdate]);
126+
}, [channel, refreshUnreadCount, forceUpdate, channelListForceUpdate]);
127+
128+
const latestMessagePreview = useLatestMessagePreview(channel, forceUpdate, lastMessage);
83129

84-
return { lastMessage, muted, unread };
130+
return { latestMessagePreview, muted, unread };
85131
};

0 commit comments

Comments
 (0)