Skip to content

Commit eba7bbe

Browse files
authored
fix: keep channels initially without id registered for WS events (#2095)
1 parent 63c18fe commit eba7bbe

File tree

12 files changed

+257
-63
lines changed

12 files changed

+257
-63
lines changed

src/components/Channel/Channel.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import debounce from 'lodash.debounce';
1212
import throttle from 'lodash.throttle';
1313
import {
1414
ChannelAPIResponse,
15+
ChannelMemberResponse,
1516
ChannelState,
1617
Event,
1718
logChatPromiseExecution,
@@ -68,6 +69,7 @@ import {
6869
import { hasMoreMessagesProbably, hasNotMoreMessages } from '../MessageList/utils';
6970
import defaultEmojiData from '../../stream-emoji.json';
7071
import { makeAddNotifications } from './utils';
72+
import { getChannel } from '../../utils/getChannel';
7173

7274
import type { Data as EmojiMartData } from 'emoji-mart';
7375

@@ -478,7 +480,25 @@ const ChannelInner = <
478480
(async () => {
479481
if (!channel.initialized) {
480482
try {
481-
await channel.watch();
483+
// if active channel has been set without id, we will create a temporary channel id from its member IDs
484+
// to keep track of the /query request in progress. This is the same approach of generating temporary id
485+
// that the JS client uses to keep track of channel in client.activeChannels
486+
const members: string[] = [];
487+
if (!channel.id && channel.data?.members) {
488+
for (const member of channel.data.members) {
489+
let userId: string | undefined;
490+
if (typeof member === 'string') {
491+
userId = member;
492+
} else if (typeof member === 'object') {
493+
const { user, user_id } = member as ChannelMemberResponse<StreamChatGenerics>;
494+
userId = user_id || user?.id;
495+
}
496+
if (userId) {
497+
members.push(userId);
498+
}
499+
}
500+
}
501+
await getChannel({ channel, client, members });
482502
const config = channel.getConfig();
483503
setChannelConfig(config);
484504
} catch (e) {

src/components/ChannelList/hooks/useChannelVisibleListener.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect } from 'react';
22
import uniqBy from 'lodash.uniqby';
33

4-
import { getChannel } from '../utils';
4+
import { getChannel } from '../../../utils/getChannel';
55

66
import { useChatContext } from '../../../context/ChatContext';
77

@@ -25,7 +25,11 @@ export const useChannelVisibleListener = <
2525
if (customHandler && typeof customHandler === 'function') {
2626
customHandler(setChannels, event);
2727
} else if (event.type && event.channel_type && event.channel_id) {
28-
const channel = await getChannel(client, event.channel_type, event.channel_id);
28+
const channel = await getChannel({
29+
client,
30+
id: event.channel_id,
31+
type: event.channel_type,
32+
});
2933
setChannels((channels) => uniqBy([channel, ...channels], 'cid'));
3034
}
3135
};

src/components/ChannelList/hooks/useNotificationAddedToChannelListener.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect } from 'react';
22
import uniqBy from 'lodash.uniqby';
33

4-
import { getChannel } from '../utils';
4+
import { getChannel } from '../../../utils/getChannel';
55

66
import { useChatContext } from '../../../context/ChatContext';
77

@@ -26,7 +26,18 @@ export const useNotificationAddedToChannelListener = <
2626
if (customHandler && typeof customHandler === 'function') {
2727
customHandler(setChannels, event);
2828
} else if (allowNewMessagesFromUnfilteredChannels && event.channel?.type) {
29-
const channel = await getChannel(client, event.channel.type, event.channel.id);
29+
const channel = await getChannel({
30+
client,
31+
id: event.channel.id,
32+
members: event.channel.members?.reduce<string[]>((acc, { user, user_id }) => {
33+
const userId = user_id || user?.id;
34+
if (userId) {
35+
acc.push(userId);
36+
}
37+
return acc;
38+
}, []),
39+
type: event.channel.type,
40+
});
3041
setChannels((channels) => uniqBy([channel, ...channels], 'cid'));
3142
}
3243
};

src/components/ChannelList/hooks/useNotificationMessageNewListener.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect } from 'react';
22
import uniqBy from 'lodash.uniqby';
33

4-
import { getChannel } from '../utils';
4+
import { getChannel } from '../../../utils/getChannel';
55

66
import { useChatContext } from '../../../context/ChatContext';
77

@@ -26,7 +26,11 @@ export const useNotificationMessageNewListener = <
2626
if (customHandler && typeof customHandler === 'function') {
2727
customHandler(setChannels, event);
2828
} else if (allowNewMessagesFromUnfilteredChannels && event.channel?.type) {
29-
const channel = await getChannel(client, event.channel.type, event.channel.id);
29+
const channel = await getChannel({
30+
client,
31+
id: event.channel.id,
32+
type: event.channel.type,
33+
});
3034
setChannels((channels) => uniqBy([channel, ...channels], 'cid'));
3135
}
3236
};

src/components/ChannelList/utils.ts

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,8 @@
1-
import type { Channel, QueryChannelAPIResponse, StreamChat } from 'stream-chat';
1+
import type { Channel } from 'stream-chat';
22
import uniqBy from 'lodash.uniqby';
33

44
import type { DefaultStreamChatGenerics } from '../../types/types';
55

6-
/**
7-
* prevent from duplicate invocation of channel.watch()
8-
* when events 'notification.message_new' and 'notification.added_to_channel' arrive at the same time
9-
*/
10-
const WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL: Record<
11-
string,
12-
Promise<QueryChannelAPIResponse> | undefined
13-
> = {};
14-
15-
/**
16-
* Calls channel.watch() if it was not already recently called. Waits for watch promise to resolve even if it was invoked previously.
17-
* @param client
18-
* @param type
19-
* @param id
20-
*/
21-
export const getChannel = async <
22-
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
23-
>(
24-
client: StreamChat<StreamChatGenerics>,
25-
type: string,
26-
id: string,
27-
) => {
28-
const channel = client.channel(type, id);
29-
const queryPromise = WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[channel.cid];
30-
if (queryPromise) {
31-
await queryPromise;
32-
} else {
33-
WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[channel.cid] = channel.watch();
34-
await WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[channel.cid];
35-
WATCH_QUERY_IN_PROGRESS_FOR_CHANNEL[channel.cid] = undefined;
36-
}
37-
38-
return channel;
39-
};
40-
416
export const MAX_QUERY_CHANNELS_LIMIT = 30;
427

438
type MoveChannelUpParams<

src/components/ChatAutoComplete/__tests__/ChatAutocomplete.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ describe('ChatAutoComplete', () => {
8383
beforeEach(async () => {
8484
const messages = [generateMessage({ user })];
8585
const members = [generateMember({ user }), generateMember({ user: mentionUser })];
86-
const mockedChannel = generateChannel({
86+
const mockedChannelData = generateChannel({
8787
members,
8888
messages,
8989
});
9090
chatClient = await getTestClientWithUser(user);
91-
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
92-
channel = chatClient.channel('messaging', mockedChannel.id);
91+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannelData)]);
92+
channel = chatClient.channel('messaging', mockedChannelData.channel.id);
9393
});
9494

9595
afterEach(cleanup);

src/components/MessageInput/__tests__/LinkPreviewList.test.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const threadMessage = generateMessage({
5555
type: 'reply',
5656
user: user1,
5757
});
58-
const mockedChannel = generateChannel({
58+
const mockedChannelData = generateChannel({
5959
members: [generateMember({ user: user1 }), generateMember({ user: mentionUser })],
6060
messages: [mainListMessage],
6161
thread: [threadMessage],
@@ -177,14 +177,14 @@ describe('Link preview', () => {
177177

178178
beforeEach(async () => {
179179
chatClient = await getTestClientWithUser({ id: user1.id });
180-
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
181-
channel = chatClient.channel('messaging', mockedChannel.id);
180+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannelData)]);
181+
channel = chatClient.channel('messaging', mockedChannelData.channel.id);
182182
});
183183

184184
afterEach(tearDown);
185185

186186
it('does not request URL enrichment if disabled in channel config', async () => {
187-
const channel = chatClient.channel('messaging', mockedChannel.id);
187+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
188188
const {
189189
channel: { config },
190190
} = generateChannel({ config: { url_enrichment: false } });
@@ -660,7 +660,7 @@ describe('Link preview', () => {
660660
});
661661

662662
it('are sent as attachments to posted message with skip_enrich_url:true', async () => {
663-
const channel = chatClient.channel('messaging', mockedChannel.id);
663+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
664664
const sendMessageSpy = jest.spyOn(channel, 'sendMessage').mockImplementation();
665665

666666
jest
@@ -716,7 +716,7 @@ describe('Link preview', () => {
716716
});
717717

718718
it('are not sent as attachments to posted message with skip_enrich_url:true if dismissed', async () => {
719-
const channel = chatClient.channel('messaging', mockedChannel.id);
719+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
720720
const sendMessageSpy = jest.spyOn(channel, 'sendMessage').mockImplementation();
721721

722722
jest
@@ -763,7 +763,7 @@ describe('Link preview', () => {
763763
});
764764

765765
it('does not add failed link previews among attachments', async () => {
766-
const channel = chatClient.channel('messaging', mockedChannel.id);
766+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
767767
const sendMessageSpy = jest.spyOn(channel, 'sendMessage').mockImplementation();
768768

769769
jest
@@ -804,7 +804,7 @@ describe('Link preview', () => {
804804
});
805805

806806
it('does not add dismissed link previews among attachments', async () => {
807-
const channel = chatClient.channel('messaging', mockedChannel.id);
807+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
808808
const sendMessageSpy = jest.spyOn(channel, 'sendMessage').mockImplementation();
809809
jest
810810
.spyOn(chatClient, 'enrichURL')
@@ -1001,7 +1001,7 @@ describe('Link preview', () => {
10011001
});
10021002

10031003
it('submit new message with skip_url_enrich:false if no link previews managed to get loaded', async () => {
1004-
const channel = chatClient.channel('messaging', mockedChannel.id);
1004+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
10051005
const sendMessageSpy = jest.spyOn(channel, 'sendMessage').mockImplementation();
10061006
let resolveEnrichURLPromise;
10071007
jest
@@ -1030,7 +1030,7 @@ describe('Link preview', () => {
10301030
});
10311031

10321032
it('submit updated message with skip_url_enrich:false if no link previews managed to get loaded', async () => {
1033-
const channel = chatClient.channel('messaging', mockedChannel.id);
1033+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
10341034
const scrapedAudioAttachment = generateScrapedAudioAttachment({
10351035
og_scrape_url: 'http://getstream.io/audio',
10361036
});
@@ -1348,7 +1348,7 @@ describe('Link preview', () => {
13481348
});
13491349

13501350
it('link preview state is cleared after message submission', async () => {
1351-
const channel = chatClient.channel('messaging', mockedChannel.id);
1351+
const channel = chatClient.channel('messaging', mockedChannelData.channel.id);
13521352
const sendMessageSpy = jest.spyOn(channel, 'sendMessage').mockImplementation();
13531353
let resolveEnrichURLPromise;
13541354
jest

src/components/MessageInput/__tests__/MessageInput.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const threadMessage = generateMessage({
5252
type: 'reply',
5353
user: user1,
5454
});
55-
const mockedChannel = generateChannel({
55+
const mockedChannelData = generateChannel({
5656
members: [generateMember({ user: user1 }), generateMember({ user: mentionUser })],
5757
messages: [mainListMessage],
5858
thread: [threadMessage],
@@ -162,8 +162,8 @@ function axeNoViolations(container) {
162162
describe(`${componentName}`, () => {
163163
beforeEach(async () => {
164164
chatClient = await getTestClientWithUser({ id: user1.id });
165-
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
166-
channel = chatClient.channel('messaging', mockedChannel.id);
165+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannelData)]);
166+
channel = chatClient.channel('messaging', mockedChannelData.channel.id);
167167
});
168168
afterEach(tearDown);
169169

@@ -1125,8 +1125,8 @@ function axeNoViolations(container) {
11251125
describe(`${componentName}`, () => {
11261126
beforeEach(async () => {
11271127
chatClient = await getTestClientWithUser({ id: user1.id });
1128-
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
1129-
channel = chatClient.channel('messaging', mockedChannel.id);
1128+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannelData)]);
1129+
channel = chatClient.channel('messaging', mockedChannelData.channel.id);
11301130
});
11311131

11321132
afterEach(tearDown);

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './context';
33
export * from './i18n';
44
// todo: distribute utils into separate files
55
export * from './utils';
6+
export { getChannel } from './utils/getChannel';

src/mock-builders/generator/channel.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export const generateChannel = (options = { channel: {} }) => {
44
const { channel: optionsChannel, config, ...optionsBesidesChannel } = options;
55
const id = optionsChannel?.id ?? nanoid();
66
const type = optionsChannel?.type ?? 'messaging';
7+
const { id: _, type: __, ...restOptionsChannel } = optionsChannel ?? {};
8+
79
return {
810
members: [],
911
messages: [],
@@ -56,7 +58,7 @@ export const generateChannel = (options = { channel: {} }) => {
5658
id,
5759
type,
5860
updated_at: '2020-04-28T11:20:48.578147Z',
59-
...optionsChannel,
61+
...restOptionsChannel,
6062
},
6163
};
6264
};

0 commit comments

Comments
 (0)