Skip to content

Commit 6237678

Browse files
authored
Fix listeners for chat events of AzureCommunicationCallWithChatAdapter when the threadId changes (#5764)
* Fix listeners subscribed to chat events of AzureCallWithChatAdapter when the thread id is resolved late or is changed when moving to and from breakout rooms * test code * Revert "test code" This reverts commit 5e1c043. * Change files * fix errors * refactor chatEventListeners and updateChatEventListeners * Clear chat event listeners on dispose of CallWithChatAdapter
1 parent 3fc6a0a commit 6237678

3 files changed

Lines changed: 71 additions & 0 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "feature",
4+
"workstream": "Breakout rooms",
5+
"comment": "Fix listeners subscribed to chat events of AzureCallWithChatAdapter when the thread id is resolved late or is changed when moving to and from breakout rooms",
6+
"packageName": "@azure/communication-react",
7+
"email": "79475487+mgamis-msft@users.noreply.github.com",
8+
"dependentChangeType": "patch"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "feature",
4+
"workstream": "Breakout rooms",
5+
"comment": "Fix listeners subscribed to chat events of AzureCallWithChatAdapter when the thread id is resolved late or is changed when moving to and from breakout rooms",
6+
"packageName": "@azure/communication-react",
7+
"email": "79475487+mgamis-msft@users.noreply.github.com",
8+
"dependentChangeType": "patch"
9+
}

packages/react-composites/src/composites/CallWithChatComposite/adapter/AzureCommunicationCallWithChatAdapter.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ import { busyWait } from '../../common/utils';
112112

113113
type CallWithChatAdapterStateChangedHandler = (newState: CallWithChatAdapterState) => void;
114114

115+
type Listener = (...args: unknown[]) => void;
116+
115117
/**
116118
* For each time that we use the hook {@link useSelector} in the {@link CallWithChatComposite} we add another listener
117119
* to the `stateChanged` event on the this adapter. This number is set in relation to the number of
@@ -181,6 +183,27 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
181183
private originCallChatAdapter: ChatAdapter | undefined;
182184
private breakoutRoomChatAdapter: ChatAdapter | undefined;
183185

186+
// This map is used to store the listeners subscribed to chat events so that we can re-subscribe them
187+
// to the new chat adapter when the thread id changes
188+
private chatEventListeners: Map<string, Set<Listener>> = new Map();
189+
190+
// This function is used to store a listener to the internal map of chat event listeners and should be
191+
// used when a listener subscribes to a chat event
192+
private storeChatEventListener(event: string, listener: Listener): void {
193+
if (!this.chatEventListeners.has(event)) {
194+
this.chatEventListeners.set(event, new Set());
195+
}
196+
this.chatEventListeners.get(event)?.add(listener);
197+
}
198+
199+
// This function is used to remove a listener from the internal map of chat event listeners and should
200+
// be used when a listener unsubscribes from a chat event
201+
private removeChatEventListener(event: string, listener: Listener): void {
202+
if (this.chatEventListeners.has(event)) {
203+
this.chatEventListeners.get(event)?.delete(listener);
204+
}
205+
}
206+
184207
constructor(callAdapter: CallAdapter, chatAdapter?: ChatAdapter) {
185208
this.bindPublicMethods();
186209
this.callAdapter = callAdapter;
@@ -315,12 +338,24 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
315338

316339
private updateChatAdapter(chatAdapter: ChatAdapter): void {
317340
this.chatAdapter?.offStateChange(this.onChatStateChange);
341+
this.updateChatEventListeners(chatAdapter);
318342
this.chatAdapter = chatAdapter;
319343
this.chatAdapter.onStateChange(this.onChatStateChange);
320344
this.context.updateClientStateWithChatState(chatAdapter.getState());
321345
this.emitter.emit('chatInitialized', this.chatAdapter);
322346
}
323347

348+
private updateChatEventListeners(chatAdapter: ChatAdapter): void {
349+
for (const [eventName, listeners] of this.chatEventListeners) {
350+
for (const listener of listeners) {
351+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
352+
this.chatAdapter?.off(eventName as any, listener);
353+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
354+
chatAdapter.on(eventName as any, listener);
355+
}
356+
}
357+
}
358+
324359
private bindPublicMethods(): void {
325360
this.joinCall.bind(this);
326361
this.leaveCall.bind(this);
@@ -439,6 +474,8 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
439474
}
440475
this.callAdapter.offStateChange(this.onCallStateChange);
441476
this.callAdapter.dispose();
477+
// Clear chat event listeners
478+
this.chatEventListeners.clear();
442479
}
443480
/** Remove a participant from the Call only. */
444481
public async removeParticipant(userId: string | CommunicationIdentifier): Promise<void> {
@@ -848,36 +885,43 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
848885
this.callAdapter.on('isSpokenLanguageChanged', listener);
849886
break;
850887
case 'messageReceived':
888+
this.storeChatEventListener('messageReceived', listener);
851889
this.executeWithResolvedChatAdapter((adapter) => {
852890
adapter.on('messageReceived', listener);
853891
});
854892
break;
855893
case 'messageEdited':
894+
this.storeChatEventListener('messageEdited', listener);
856895
this.executeWithResolvedChatAdapter((adapter) => {
857896
adapter.on('messageEdited', listener);
858897
});
859898
break;
860899
case 'messageDeleted':
900+
this.storeChatEventListener('messageDeleted', listener);
861901
this.executeWithResolvedChatAdapter((adapter) => {
862902
adapter.on('messageDeleted', listener);
863903
});
864904
break;
865905
case 'messageSent':
906+
this.storeChatEventListener('messageSent', listener);
866907
this.executeWithResolvedChatAdapter((adapter) => {
867908
adapter.on('messageSent', listener);
868909
});
869910
break;
870911
case 'messageRead':
912+
this.storeChatEventListener('messageRead', listener);
871913
this.executeWithResolvedChatAdapter((adapter) => {
872914
adapter.on('messageRead', listener);
873915
});
874916
break;
875917
case 'chatParticipantsAdded':
918+
this.storeChatEventListener('chatParticipantsAdded', listener);
876919
this.executeWithResolvedChatAdapter((adapter) => {
877920
adapter.on('participantsAdded', listener);
878921
});
879922
break;
880923
case 'chatParticipantsRemoved':
924+
this.storeChatEventListener('chatParticipantsRemoved', listener);
881925
this.executeWithResolvedChatAdapter((adapter) => {
882926
adapter.on('participantsRemoved', listener);
883927
});
@@ -886,6 +930,7 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
886930
this.callAdapter.on('error', listener);
887931
break;
888932
case 'chatError':
933+
this.storeChatEventListener('chatError', listener);
889934
this.executeWithResolvedChatAdapter((adapter) => {
890935
adapter.on('error', listener);
891936
});
@@ -986,36 +1031,43 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
9861031
this.callAdapter.off('isSpokenLanguageChanged', listener);
9871032
break;
9881033
case 'messageReceived':
1034+
this.removeChatEventListener('messageReceived', listener);
9891035
this.executeWithResolvedChatAdapter((adapter) => {
9901036
adapter.off('messageReceived', listener);
9911037
});
9921038
break;
9931039
case 'messageEdited':
1040+
this.removeChatEventListener('messageEdited', listener);
9941041
this.executeWithResolvedChatAdapter((adapter) => {
9951042
adapter.off('messageEdited', listener);
9961043
});
9971044
break;
9981045
case 'messageDeleted':
1046+
this.removeChatEventListener('messageDeleted', listener);
9991047
this.executeWithResolvedChatAdapter((adapter) => {
10001048
adapter.off('messageDeleted', listener);
10011049
});
10021050
break;
10031051
case 'messageSent':
1052+
this.removeChatEventListener('messageSent', listener);
10041053
this.executeWithResolvedChatAdapter((adapter) => {
10051054
adapter.off('messageSent', listener);
10061055
});
10071056
break;
10081057
case 'messageRead':
1058+
this.removeChatEventListener('messageRead', listener);
10091059
this.executeWithResolvedChatAdapter((adapter) => {
10101060
adapter.off('messageRead', listener);
10111061
});
10121062
break;
10131063
case 'chatParticipantsAdded':
1064+
this.removeChatEventListener('chatParticipantsAdded', listener);
10141065
this.executeWithResolvedChatAdapter((adapter) => {
10151066
adapter.off('participantsAdded', listener);
10161067
});
10171068
break;
10181069
case 'chatParticipantsRemoved':
1070+
this.removeChatEventListener('chatParticipantsRemoved', listener);
10191071
this.executeWithResolvedChatAdapter((adapter) => {
10201072
adapter.off('participantsRemoved', listener);
10211073
});
@@ -1024,6 +1076,7 @@ export class AzureCommunicationCallWithChatAdapter implements CallWithChatAdapte
10241076
this.callAdapter.off('error', listener);
10251077
break;
10261078
case 'chatError':
1079+
this.removeChatEventListener('chatError', listener);
10271080
this.executeWithResolvedChatAdapter((adapter) => {
10281081
adapter.off('error', listener);
10291082
});

0 commit comments

Comments
 (0)