diff --git a/src/channel.ts b/src/channel.ts index bded4c430..11a0d9f5e 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -1661,8 +1661,10 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene ) { const { state: clientState, user, userID } = this.getClient(); - // add the Users + // add the members and users if (state.members) { + this._hydrateMembers({ members: state.members }); + for (const member of state.members) { if (member.user) { clientState.updateUserReference(member.user, this.cid); @@ -1728,10 +1730,6 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene } } - if (state.members) { - this._hydrateMembers(state.members); - } - return { messageSet, }; @@ -1747,13 +1745,34 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene } } - _hydrateMembers(members: ChannelMemberResponse<StreamChatGenerics>[]) { - this.state.members = members.reduce((acc, member) => { + _hydrateMembers({ + members, + overrideCurrentState = true, + }: { + members: ChannelMemberResponse<StreamChatGenerics>[]; + /** + * If set to `true` then `ChannelState.members` will be overriden with the newly + * provided `members`, setting this property to `false` will merge current `ChannelState.members` + * object with the newly provided `members` + * (new members with the same `userId` will replace the old ones). + */ + overrideCurrentState?: boolean; + }) { + const newMembersById = members.reduce<ChannelState<StreamChatGenerics>['members']>((membersById, member) => { if (member.user) { - acc[member.user.id] = member; + membersById[member.user.id] = member; } - return acc; - }, {} as ChannelState<StreamChatGenerics>['members']); + return membersById; + }, {}); + + if (overrideCurrentState) { + this.state.members = newMembersById; + } else if (!overrideCurrentState && members.length) { + this.state.members = { + ...this.state.members, + ...newMembersById, + }; + } } _disconnect() { diff --git a/src/thread.ts b/src/thread.ts index 13874da67..f8f39beef 100644 --- a/src/thread.ts +++ b/src/thread.ts @@ -78,7 +78,7 @@ export class Thread<SCG extends ExtendableGenerics = DefaultGenerics> { const channel = client.channel(threadData.channel.type, threadData.channel.id, { name: threadData.channel.name, }); - channel._hydrateMembers(threadData.channel.members ?? []); + channel._hydrateMembers({ members: threadData.channel.members ?? [], overrideCurrentState: false }); // For when read object is undefined and due to that unreadMessageCount for // the current user isn't being incremented on message.new diff --git a/test/unit/threads.test.ts b/test/unit/threads.test.ts index c71088991..d3c5211fa 100644 --- a/test/unit/threads.test.ts +++ b/test/unit/threads.test.ts @@ -48,7 +48,9 @@ describe('Threads 2.0', () => { beforeEach(() => { client = new StreamChat('apiKey'); client._setUser({ id: TEST_USER_ID }); - channelResponse = generateChannel({ channel: { id: uuidv4(), name: 'Test channel' } }).channel as ChannelResponse; + channelResponse = generateChannel({ + channel: { id: uuidv4(), name: 'Test channel', members: [] }, + }).channel as ChannelResponse; channel = client.channel(channelResponse.type, channelResponse.id); parentMessageResponse = generateMsg() as MessageResponse; threadManager = new ThreadManager({ client }); @@ -57,12 +59,16 @@ describe('Threads 2.0', () => { describe('Thread', () => { it('initializes properly', () => { const threadResponse = generateThreadResponse(channelResponse, parentMessageResponse); + // mimic pre-cached channel with existing members + channel._hydrateMembers({ members: [{ user: { id: TEST_USER_ID } }] }); const thread = new Thread({ client, threadData: threadResponse }); const state = thread.state.getLatestValue(); + expect(threadResponse.channel.members).to.have.lengthOf(0); expect(threadResponse.read).to.have.lengthOf(0); expect(state.read).to.have.keys([TEST_USER_ID]); + expect(thread.channel.state.members).to.have.keys([TEST_USER_ID]); expect(thread.id).to.equal(parentMessageResponse.id); expect(thread.channel.data?.name).to.equal(channelResponse.name); });