-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix for member based channel #586
Conversation
…hal/thread_participants_type
Size Change: +4.03 kB (1%) Total Size: 214 kB
|
…m/stream-chat-js into vishal/thread_participants_type
test/integration/channels.js
Outdated
const clientVish = await getTestClientForUser(userIdVish); | ||
const channelVish_copy1 = clientVish.channel('messaging', { | ||
members: ['amin', 'vishal'], | ||
}); | ||
await channelVish_copy1.watch(); | ||
const channelVish_copy2 = clientVish.channel('messaging', { | ||
members: ['vishal', 'amin'], | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kinda an edge case to this fix which itself is an edge case but if you call channel.watch
or channel.create
after instantiating the second channel you still end up with a duplicate channel in the state. (this can actually happen if different parts of the app instantiate channels at the same time)
const channelVish_copy1 = clientVish.channel('messaging', { members: ['amin', 'vishal'] });
const channelVish_copy2 = clientVish.channel('messaging', { members: ['amin', 'vishal'] });
// channelVish_copy1 !== channelVish_copy2
await channelVish_copy1.watch();
await channelVish_copy2.watch();
// channelVish_copy1 !== channelVish_copy2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm probably we should early return from watch()
function if its already being watched
async watch(options) {
if (this.initialized) {
return;
}
... // rest of the logic
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea it's a potential fix but there could be changes in the option param which cause other issues. To cover this we need to make some changes to the new code you implemented to also check for channels that don't have their cid
yet but have the same member list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense!! Have updated the PR.
So when channel doesn't have cid
yet, I am storing it in activeChannels
as following:
{
"messaging:amin,vishal": uniqueChannelAminVishal,
"messaging:k4j2n3k4j2n3kn42k3": channelWithId
}
When cid gets set on channel, I replace messaging:amin,vishal
with actual cid generated from backend.
{
"messaging:!members:kajshdkjsakjsdkjas": uniqueChannelAminVishal,
"messaging:k4j2n3k4j2n3kn42k3": channelWithId
}
Also updated tests to reflect this case :)
…m/stream-chat-js into vishal/thread_participants_type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
src/client.ts
Outdated
* Its a submethod for `client.channel()` method, used to create unique conversation or | ||
* channel based on member list instead of id. | ||
* | ||
* If the channel already exist in `activeChannels` list, then we simply return it, since that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exists
src/client.ts
Outdated
* If the channel already exist in `activeChannels` list, then we simply return it, since that | ||
* means the same channel was already requested or created. | ||
* | ||
* Otherwise we create new instance of Channel class and return it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a new
src/client.ts
Outdated
} | ||
|
||
/** | ||
* Its a submethod for `client.channel()` method, used to create unique conversation or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's
src/client.ts
Outdated
// channel could exist in `activeChannels` list with either one of the following two keys: | ||
// 1. cid - Which gets set on channel only after calling channel.query or channel.watch or channel.create | ||
// 2. Sorted membersStr - E.g., "messaging:amin,vishal" OR "messaging:amin,jaap,tom" | ||
// This gets set when you create a channel, but haven't queried yet. After query, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/gets/is/
src/client.ts
Outdated
}; | ||
|
||
/** | ||
* Its a submethod for `client.channel()` method, used to channel given the id of channel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
submethod
doesn't sound intuitive, a helper
maybe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, let's add a @private
to these two functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done :)
src/client.ts
Outdated
/** | ||
* Its a submethod for `client.channel()` method, used to channel given the id of channel. | ||
* | ||
* If the channel already exist in `activeChannels` list, then we simply return it, since that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exists
…m/stream-chat-js into vishal/thread_participants_type
…m/stream-chat-js into vishal/thread_participants_type
@mahboubii added |
src/channel.ts
Outdated
const membersStr = Object.keys(this.state.members)?.sort().join(','); | ||
|
||
if (`${this.type}:!members-${membersStr}` in this.getClient().activeChannels) { | ||
delete this.getClient().activeChannels[membersStr]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we delete the first instance from the activeChannels?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? Its a map, so there will be only one instance.
But just noticed that we are not deleting the correct key from activeChannels. It should be something like:
delete this.getClient().activeChannels[`${this.type}:!members-${membersStr}`];
Gonna add to test as well :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done !!
Moved tests to unit-test :) |
Submit a pull request
CLA
Description of the pull request
Existing logic:
We maintain a list of active channels on client -
client.activeChannel
.So every-time, user requests a channel using
client.channel('messaging', 'random_channel_id')
, we first check if thecid
already exist inclient.activeChannels
|--> if yes, then return the channel available on client.activeChannels
|--> if not, then create new instance of Channel class and return that.
This way we ensure that user doesn't end up creating multiple copies of same channel. And this is important, because when client receives ws event, it delegates it to all channels in
activeChannel
map. So if your channel isn't part ofactiveChannels
, it won't receive any of the events.Issue
This logic only worked for id based channels. If user requests a member based channel (e.g.,
client.channel('messaging', { members: ['vishal', 'amin'] })
), then we skipped that check, because the logic for generating id from members list is on backend side, and its not a good idea to expose it (checked with @thesyncim ) on frontend. So we couldn't really check if the channel already exists inclient.activeChannels
Fix
We are adding this missing check for member based channels.
For member based channel, we don't get cid until channel is watched/queried. So until we get the key from backend, we are going to store it in
activeChannels
with the key${channelType}:!members-${members.sort().join(',')}
. E.g.messaging:!members-amin,vishal
ormessaging:!members-amin,jaap,tom
. So that we can ensure uniqueness even if channel is not queries/watched yet.We assert that a particular channel in
activeChannels
is same as requested channel by user:${channelType}:!members-${members.sort().join(',')}
OR
${channelType}:!members-
AND