agora-appbuilder-core 4.1.8 → 4.1.9-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,21 +9,32 @@
9
9
  information visit https://appbuilder.agora.io.
10
10
  *********************************************
11
11
  */
12
- // @ts-nocheck
12
+
13
13
  import React, {useState, useContext, useEffect, useRef} from 'react';
14
- import RtmEngine, {RtmChannelAttribute} from 'agora-react-native-rtm';
14
+ import {
15
+ type GetChannelMetadataResponse,
16
+ type GetOnlineUsersResponse,
17
+ type LinkStateEvent,
18
+ type MessageEvent,
19
+ type Metadata,
20
+ type PresenceEvent,
21
+ type SetOrUpdateUserMetadataOptions,
22
+ type StorageEvent,
23
+ type RTMClient,
24
+ type GetUserMetadataResponse,
25
+ } from 'agora-react-native-rtm';
15
26
  import {
16
27
  ContentInterface,
17
28
  DispatchContext,
18
29
  PropsContext,
30
+ UidType,
19
31
  useLocalUid,
20
32
  } from '../../agora-rn-uikit';
21
33
  import ChatContext from './ChatContext';
22
34
  import {Platform} from 'react-native';
23
35
  import {backOff} from 'exponential-backoff';
24
- import {useString} from '../utils/useString';
25
36
  import {isAndroid, isIOS, isWeb, isWebInternal} from '../utils/common';
26
- import {useContent, useIsAttendee, useUserName} from 'customization-api';
37
+ import {useContent} from 'customization-api';
27
38
  import {
28
39
  safeJsonParse,
29
40
  timeNow,
@@ -31,8 +42,8 @@ import {
31
42
  getMessageTime,
32
43
  get32BitUid,
33
44
  } from '../rtm/utils';
34
- import {EventUtils, EventsQueue, EventNames} from '../rtm-events';
35
- import events, {PersistanceLevel} from '../rtm-events-api';
45
+ import {EventUtils, EventsQueue} from '../rtm-events';
46
+ import {PersistanceLevel} from '../rtm-events-api';
36
47
  import RTMEngine from '../rtm/RTMEngine';
37
48
  import {filterObject} from '../utils';
38
49
  import SDKEvents from '../utils/SdkEvents';
@@ -45,60 +56,79 @@ import {
45
56
  import LocalEventEmitter, {
46
57
  LocalEventsEnum,
47
58
  } from '../rtm-events-api/LocalEvents';
48
- import {PSTNUserLabel} from '../language/default-labels/videoCallScreenLabels';
49
59
  import {controlMessageEnum} from '../components/ChatContext';
50
60
  import {LogSource, logger} from '../logger/AppBuilderLogger';
51
61
  import {RECORDING_BOT_UID} from '../utils/constants';
62
+ import {
63
+ nativeChannelTypeMapping,
64
+ nativeLinkStateMapping,
65
+ nativePresenceEventTypeMapping,
66
+ nativeStorageEventTypeMapping,
67
+ } from '../../bridge/rtm/web/Types';
52
68
 
53
69
  export enum UserType {
54
70
  ScreenShare = 'screenshare',
55
71
  }
56
72
 
73
+ const eventTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
74
+
57
75
  const RtmConfigure = (props: any) => {
76
+ let engine = useRef<RTMClient>(null!);
58
77
  const rtmInitTimstamp = new Date().getTime();
59
78
  const localUid = useLocalUid();
60
79
  const {callActive} = props;
61
80
  const {rtcProps} = useContext(PropsContext);
62
81
  const {dispatch} = useContext(DispatchContext);
63
82
  const {defaultContent, activeUids} = useContent();
64
- const defaultContentRef = useRef({defaultContent: defaultContent});
65
- const activeUidsRef = useRef({activeUids: activeUids});
66
-
67
83
  const {
68
84
  waitingRoomStatus,
69
85
  data: {isHost},
70
86
  } = useRoomInfo();
71
- const waitingRoomStatusRef = useRef({waitingRoomStatus: waitingRoomStatus});
87
+ const [hasUserJoinedRTM, setHasUserJoinedRTM] = useState<boolean>(false);
88
+ const [isInitialQueueCompleted, setIsInitialQueueCompleted] = useState(false);
89
+ const [onlineUsersCount, setTotalOnlineUsers] = useState<number>(0);
90
+ const timerValueRef: any = useRef(5);
91
+ // Track RTM connection state (equivalent to v1.5x connectionState check)
92
+ const [rtmConnectionState, setRtmConnectionState] = useState<number>(0); // 0=IDLE, 2=CONNECTED
72
93
 
94
+ /**
95
+ * inside event callback state won't have latest value.
96
+ * so creating ref to access the state
97
+ */
73
98
  const isHostRef = useRef({isHost: isHost});
74
-
75
99
  useEffect(() => {
76
100
  isHostRef.current.isHost = isHost;
77
101
  }, [isHost]);
78
102
 
103
+ const waitingRoomStatusRef = useRef({waitingRoomStatus: waitingRoomStatus});
79
104
  useEffect(() => {
80
105
  waitingRoomStatusRef.current.waitingRoomStatus = waitingRoomStatus;
81
106
  }, [waitingRoomStatus]);
82
107
 
83
- /**
84
- * inside event callback state won't have latest value.
85
- * so creating ref to access the state
86
- */
108
+ const activeUidsRef = useRef({activeUids: activeUids});
87
109
  useEffect(() => {
88
110
  activeUidsRef.current.activeUids = activeUids;
89
111
  }, [activeUids]);
90
112
 
113
+ const defaultContentRef = useRef({defaultContent: defaultContent});
91
114
  useEffect(() => {
92
115
  defaultContentRef.current.defaultContent = defaultContent;
93
116
  }, [defaultContent]);
94
117
 
95
- const [hasUserJoinedRTM, setHasUserJoinedRTM] = useState<boolean>(false);
96
- const [isInitialQueueCompleted, setIsInitialQueueCompleted] = useState(false);
97
- const [onlineUsersCount, setTotalOnlineUsers] = useState<number>(0);
98
-
99
- let engine = useRef<RtmEngine>(null!);
100
- const timerValueRef: any = useRef(5);
118
+ // Eventdispatcher timeout refs clean
119
+ const isRTMMounted = useRef(true);
120
+ useEffect(() => {
121
+ return () => {
122
+ isRTMMounted.current = false;
123
+ // Clear all pending timeouts on unmount
124
+ for (const timeout of eventTimeouts.values()) {
125
+ clearTimeout(timeout);
126
+ }
127
+ eventTimeouts.clear();
128
+ };
129
+ }, []);
101
130
 
131
+ // Set online users
102
132
  React.useEffect(() => {
103
133
  setTotalOnlineUsers(
104
134
  Object.keys(
@@ -107,31 +137,42 @@ const RtmConfigure = (props: any) => {
107
137
  ([k, v]) =>
108
138
  v?.type === 'rtc' &&
109
139
  !v.offline &&
110
- activeUids.indexOf(v?.uid) !== -1,
140
+ activeUidsRef.current.activeUids.indexOf(v?.uid) !== -1,
111
141
  ),
112
142
  ).length,
113
143
  );
114
144
  }, [defaultContent]);
115
145
 
116
146
  React.useEffect(() => {
117
- if (!$config.ENABLE_CONVERSATIONAL_AI) {
147
+ // If its not a convo ai project and
148
+ // the platform is web execute the window listeners
149
+ if (!$config.ENABLE_CONVERSATIONAL_AI && isWebInternal()) {
118
150
  const handBrowserClose = ev => {
119
151
  ev.preventDefault();
120
152
  return (ev.returnValue = 'Are you sure you want to exit?');
121
153
  };
122
154
  const logoutRtm = () => {
123
- engine.current.leaveChannel(rtcProps.channel);
155
+ try {
156
+ if (engine.current && RTMEngine.getInstance().channelUid) {
157
+ // First unsubscribe from channel (like v1.5x leaveChannel)
158
+ engine.current.unsubscribe(RTMEngine.getInstance().channelUid);
159
+ // Then logout
160
+ engine.current.logout();
161
+ }
162
+ } catch (error) {
163
+ console.error('Error during browser close RTM cleanup:', error);
164
+ }
124
165
  };
125
166
 
126
- if (!isWebInternal()) return;
167
+ // Set up window listeners
127
168
  window.addEventListener(
128
169
  'beforeunload',
129
170
  isWeb() && !isSDK() ? handBrowserClose : () => {},
130
171
  );
131
172
 
132
173
  window.addEventListener('pagehide', logoutRtm);
133
- // cleanup this component
134
174
  return () => {
175
+ // Remove listeners on unmount
135
176
  window.removeEventListener(
136
177
  'beforeunload',
137
178
  isWeb() && !isSDK() ? handBrowserClose : () => {},
@@ -141,23 +182,303 @@ const RtmConfigure = (props: any) => {
141
182
  }
142
183
  }, []);
143
184
 
185
+ const init = async (rtcUid: UidType) => {
186
+ //on sdk due to multiple re-render we are getting rtm error code 8
187
+ //you are joining the same channel too frequently, exceeding the allowed rate of joining the same channel multiple times within a short period
188
+ //so checking rtm connection state before proceed
189
+
190
+ // Check if already connected (equivalent to v1.5x connectionState === 'CONNECTED')
191
+ if (
192
+ rtmConnectionState === nativeLinkStateMapping.CONNECTED &&
193
+ RTMEngine.getInstance().isEngineReady
194
+ ) {
195
+ logger.log(
196
+ LogSource.AgoraSDK,
197
+ 'Log',
198
+ '🚫 RTM already connected, skipping initialization',
199
+ );
200
+ return;
201
+ }
202
+
203
+ try {
204
+ if (!RTMEngine.getInstance().isEngineReady) {
205
+ RTMEngine.getInstance().setLocalUID(rtcUid);
206
+ logger.log(LogSource.AgoraSDK, 'API', 'RTM local Uid set', rtcUid);
207
+ }
208
+ engine.current = RTMEngine.getInstance().engine;
209
+ // Logout any opened sessions if any
210
+ engine.current.logout();
211
+ logger.log(LogSource.AgoraSDK, 'Log', 'RTM client creation done');
212
+ } catch (error) {
213
+ logger.error(
214
+ LogSource.AgoraSDK,
215
+ 'Log',
216
+ 'RTM engine initialization failed:',
217
+ {error},
218
+ );
219
+ throw error;
220
+ }
221
+
222
+ engine.current.addEventListener(
223
+ 'linkState',
224
+ async (data: LinkStateEvent) => {
225
+ // Update connection state for duplicate initialization prevention
226
+ setRtmConnectionState(data.currentState);
227
+ logger.log(
228
+ LogSource.AgoraSDK,
229
+ 'Event',
230
+ `RTM linkState changed: ${data.previousState} -> ${data.currentState}`,
231
+ data,
232
+ );
233
+ if (data.currentState === nativeLinkStateMapping.CONNECTED) {
234
+ // CONNECTED state
235
+ logger.log(LogSource.AgoraSDK, 'Event', 'RTM connected', {
236
+ previousState: data.previousState,
237
+ currentState: data.currentState,
238
+ });
239
+ }
240
+ if (data.currentState === nativeLinkStateMapping.FAILED) {
241
+ // FAILED state
242
+ logger.error(LogSource.AgoraSDK, 'Event', 'RTM connection failed', {
243
+ error: {
244
+ reasonCode: data.reasonCode,
245
+ currentState: data.currentState,
246
+ },
247
+ });
248
+ }
249
+ },
250
+ );
251
+
252
+ engine.current.addEventListener('storage', (storage: StorageEvent) => {
253
+ // when remote user sets/updates metadata - 3
254
+ if (
255
+ storage.eventType === nativeStorageEventTypeMapping.SET ||
256
+ storage.eventType === nativeStorageEventTypeMapping.UPDATE
257
+ ) {
258
+ const storageTypeStr = storage.storageType === 1 ? 'user' : 'channel';
259
+ const eventTypeStr = storage.eventType === 2 ? 'SET' : 'UPDATE';
260
+ logger.log(
261
+ LogSource.AgoraSDK,
262
+ 'Event',
263
+ `RTM storage event of type: [${eventTypeStr} ${storageTypeStr} metadata]`,
264
+ storage,
265
+ );
266
+ try {
267
+ if (storage.data?.items && Array.isArray(storage.data.items)) {
268
+ storage.data.items.forEach(item => {
269
+ try {
270
+ if (!item || !item.key) {
271
+ logger.warn(
272
+ LogSource.Events,
273
+ 'CUSTOM_EVENTS',
274
+ 'Invalid storage item:',
275
+ item,
276
+ );
277
+ return;
278
+ }
279
+
280
+ const {key, value, authorUserId, updateTs} = item;
281
+ const timestamp = getMessageTime(updateTs);
282
+ const sender = Platform.OS
283
+ ? get32BitUid(authorUserId)
284
+ : parseInt(authorUserId, 10);
285
+ eventDispatcher(
286
+ {
287
+ evt: key,
288
+ value,
289
+ },
290
+ `${sender}`,
291
+ timestamp,
292
+ );
293
+ } catch (error) {
294
+ logger.error(
295
+ LogSource.Events,
296
+ 'CUSTOM_EVENTS',
297
+ `Failed to process storage item: ${JSON.stringify(item)}`,
298
+ {error},
299
+ );
300
+ }
301
+ });
302
+ }
303
+ } catch (error) {
304
+ logger.error(
305
+ LogSource.Events,
306
+ 'CUSTOM_EVENTS',
307
+ 'error while dispatching through eventDispatcher',
308
+ {error},
309
+ );
310
+ }
311
+ }
312
+ });
313
+
314
+ engine.current.addEventListener(
315
+ 'presence',
316
+ async (presence: PresenceEvent) => {
317
+ if (`${localUid}` === presence.publisher) {
318
+ return;
319
+ }
320
+ // remoteJoinChannel
321
+ if (presence.type === nativePresenceEventTypeMapping.REMOTE_JOIN) {
322
+ logger.log(
323
+ LogSource.AgoraSDK,
324
+ 'Event',
325
+ 'RTM presenceEvent of type [3 - remoteJoin] (channelMemberJoined)',
326
+ );
327
+ const backoffAttributes = await fetchUserAttributesWithBackoffRetry(
328
+ presence.publisher,
329
+ );
330
+ await processUserUidAttributes(backoffAttributes, presence.publisher);
331
+ }
332
+ // remoteLeaveChannel
333
+ if (presence.type === nativePresenceEventTypeMapping.REMOTE_LEAVE) {
334
+ logger.log(
335
+ LogSource.AgoraSDK,
336
+ 'Event',
337
+ 'RTM presenceEvent of type [4 - remoteLeave] (channelMemberLeft)',
338
+ presence,
339
+ );
340
+ // Chat of left user becomes undefined. So don't cleanup
341
+ const uid = presence?.publisher
342
+ ? parseInt(presence.publisher, 10)
343
+ : undefined;
344
+
345
+ if (!uid) {
346
+ return;
347
+ }
348
+ SDKEvents.emit('_rtm-left', uid);
349
+ // updating the rtc data
350
+ updateRenderListState(uid, {
351
+ offline: true,
352
+ });
353
+ }
354
+ },
355
+ );
356
+
357
+ engine.current.addEventListener('message', (message: MessageEvent) => {
358
+ if (`${localUid}` === message.publisher) {
359
+ return;
360
+ }
361
+ // message - 1 (channel)
362
+ if (message.channelType === nativeChannelTypeMapping.MESSAGE) {
363
+ logger.debug(
364
+ LogSource.Events,
365
+ 'CUSTOM_EVENTS',
366
+ 'messageEvent of type [1 - CHANNEL] (channelMessageReceived)',
367
+ message,
368
+ );
369
+ const {
370
+ publisher: uid,
371
+ channelName: channelId,
372
+ message: text,
373
+ timestamp: ts,
374
+ } = message;
375
+ //whiteboard upload
376
+ if (parseInt(uid, 10) === 1010101) {
377
+ const [err, res] = safeJsonParse(text);
378
+ if (err) {
379
+ logger.error(
380
+ LogSource.Events,
381
+ 'CUSTOM_EVENTS',
382
+ 'JSON payload incorrect, Error while parsing the payload',
383
+ {error: err},
384
+ );
385
+ }
386
+ if (res?.data?.data?.images) {
387
+ LocalEventEmitter.emit(
388
+ LocalEventsEnum.WHITEBOARD_FILE_UPLOAD,
389
+ res?.data?.data?.images,
390
+ );
391
+ }
392
+ } else {
393
+ const [err, msg] = safeJsonParse(text);
394
+ if (err) {
395
+ logger.error(
396
+ LogSource.Events,
397
+ 'CUSTOM_EVENTS',
398
+ 'JSON payload incorrect, Error while parsing the payload',
399
+ {error: err},
400
+ );
401
+ }
402
+
403
+ const timestamp = getMessageTime(ts);
404
+ const sender = Platform.OS ? get32BitUid(uid) : parseInt(uid, 10);
405
+
406
+ if (channelId === rtcProps.channel) {
407
+ try {
408
+ eventDispatcher(msg, `${sender}`, timestamp);
409
+ } catch (error) {
410
+ logger.error(
411
+ LogSource.Events,
412
+ 'CUSTOM_EVENTS',
413
+ 'error while dispatching through eventDispatcher',
414
+ {error},
415
+ );
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // message - 3 (user)
422
+ if (message.channelType === nativeChannelTypeMapping.USER) {
423
+ logger.debug(
424
+ LogSource.Events,
425
+ 'CUSTOM_EVENTS',
426
+ 'messageEvent of type [3- USER] (messageReceived)',
427
+ message,
428
+ );
429
+ const {publisher: peerId, timestamp: ts, message: text} = message;
430
+ const [err, msg] = safeJsonParse(text);
431
+ if (err) {
432
+ logger.error(
433
+ LogSource.Events,
434
+ 'CUSTOM_EVENTS',
435
+ 'JSON payload incorrect, Error while parsing the payload',
436
+ {error: err},
437
+ );
438
+ }
439
+
440
+ const timestamp = getMessageTime(ts);
441
+
442
+ const sender = isAndroid() ? get32BitUid(peerId) : parseInt(peerId, 10);
443
+
444
+ try {
445
+ eventDispatcher(msg, `${sender}`, timestamp);
446
+ } catch (error) {
447
+ logger.error(
448
+ LogSource.Events,
449
+ 'CUSTOM_EVENTS',
450
+ 'error while dispatching through eventDispatcher',
451
+ {error},
452
+ );
453
+ }
454
+ }
455
+ });
456
+
457
+ await doLoginAndSetupRTM();
458
+ };
459
+
144
460
  const doLoginAndSetupRTM = async () => {
145
461
  try {
146
462
  logger.log(LogSource.AgoraSDK, 'API', 'RTM login starts');
147
463
  await engine.current.login({
148
- uid: localUid.toString(),
464
+ // @ts-ignore
149
465
  token: rtcProps.rtm,
150
466
  });
151
467
  logger.log(LogSource.AgoraSDK, 'API', 'RTM login done');
152
- RTMEngine.getInstance().setLocalUID(localUid.toString());
153
- logger.log(LogSource.AgoraSDK, 'API', 'RTM local Uid set');
154
468
  timerValueRef.current = 5;
469
+ // waiting for login to be fully connected
470
+ await new Promise(resolve => setTimeout(resolve, 500));
155
471
  await setAttribute();
156
- logger.log(LogSource.AgoraSDK, 'Log', 'RTM setting attribute done');
157
472
  } catch (error) {
158
- logger.error(LogSource.AgoraSDK, 'Log', 'RTM login failed..Trying again');
473
+ logger.error(
474
+ LogSource.AgoraSDK,
475
+ 'Log',
476
+ 'RTM login failed..Trying again',
477
+ {error},
478
+ );
159
479
  setTimeout(async () => {
160
- timerValueRef.current = timerValueRef.current + timerValueRef.current;
480
+ // Cap the timer to prevent excessive delays (max 30 seconds)
481
+ timerValueRef.current = Math.min(timerValueRef.current * 2, 30);
161
482
  doLoginAndSetupRTM();
162
483
  }, timerValueRef.current * 1000);
163
484
  }
@@ -169,7 +490,13 @@ const RtmConfigure = (props: any) => {
169
490
  {key: 'isHost', value: String(isHostRef.current.isHost)},
170
491
  ];
171
492
  try {
172
- await engine.current.setLocalUserAttributes(rtmAttributes);
493
+ const data: Metadata = {
494
+ items: rtmAttributes,
495
+ };
496
+ const options: SetOrUpdateUserMetadataOptions = {
497
+ userId: `${localUid}`,
498
+ };
499
+ await engine.current.storage.setUserMetadata(data, options);
173
500
  logger.log(
174
501
  LogSource.AgoraSDK,
175
502
  'API',
@@ -179,10 +506,15 @@ const RtmConfigure = (props: any) => {
179
506
  },
180
507
  );
181
508
  timerValueRef.current = 5;
182
- await joinChannel();
183
- logger.log(LogSource.AgoraSDK, 'Log', 'RTM join channel done', {
184
- data: rtmAttributes,
185
- });
509
+ await subscribeChannel();
510
+ logger.log(
511
+ LogSource.AgoraSDK,
512
+ 'Log',
513
+ 'RTM subscribe, fetch members, reading channel atrributes all done',
514
+ {
515
+ data: rtmAttributes,
516
+ },
517
+ );
186
518
  setHasUserJoinedRTM(true);
187
519
  await runQueuedEvents();
188
520
  setIsInitialQueueCompleted(true);
@@ -199,184 +531,138 @@ const RtmConfigure = (props: any) => {
199
531
  LogSource.AgoraSDK,
200
532
  'Log',
201
533
  'RTM setAttribute failed..Trying again',
534
+ {error},
202
535
  );
203
536
  setTimeout(async () => {
204
- timerValueRef.current = timerValueRef.current + timerValueRef.current;
537
+ // Cap the timer to prevent excessive delays (max 30 seconds)
538
+ timerValueRef.current = Math.min(timerValueRef.current * 2, 30);
205
539
  setAttribute();
206
540
  }, timerValueRef.current * 1000);
207
541
  }
208
542
  };
209
543
 
210
- const joinChannel = async () => {
544
+ const subscribeChannel = async () => {
211
545
  try {
212
- if (RTMEngine.getInstance().channelUid !== rtcProps.channel) {
213
- await engine.current.joinChannel(rtcProps.channel);
214
- logger.log(LogSource.AgoraSDK, 'API', 'RTM joinChannel', {
546
+ if (RTMEngine.getInstance().channelUid === rtcProps.channel) {
547
+ logger.debug(
548
+ LogSource.AgoraSDK,
549
+ 'Log',
550
+ '🚫 RTM already subscribed channel skipping',
551
+ rtcProps.channel,
552
+ );
553
+ } else {
554
+ await engine.current.subscribe(rtcProps.channel, {
555
+ withMessage: true,
556
+ withPresence: true,
557
+ withMetadata: true,
558
+ withLock: false,
559
+ });
560
+ logger.log(LogSource.AgoraSDK, 'API', 'RTM subscribeChannel', {
215
561
  data: rtcProps.channel,
216
562
  });
563
+
564
+ // Set channel ID AFTER successful subscribe (like v1.5x)
217
565
  RTMEngine.getInstance().setChannelId(rtcProps.channel);
218
566
  logger.log(
219
567
  LogSource.AgoraSDK,
220
568
  'API',
221
- 'RTM setChannelId',
569
+ 'RTM setChannelId as subscribe is successful',
222
570
  rtcProps.channel,
223
571
  );
572
+
224
573
  logger.debug(
225
574
  LogSource.SDK,
226
575
  'Event',
227
576
  'Emitting rtm joined',
228
577
  rtcProps.channel,
229
578
  );
579
+ // @ts-ignore
230
580
  SDKEvents.emit('_rtm-joined', rtcProps.channel);
231
- } else {
232
- logger.debug(
581
+ timerValueRef.current = 5;
582
+ await getMembers();
583
+ await readAllChannelAttributes();
584
+ logger.log(
233
585
  LogSource.AgoraSDK,
234
586
  'Log',
235
- 'RTM already joined channel skipping',
236
- rtcProps.channel,
587
+ 'RTM readAllChannelAttributes and getMembers done',
237
588
  );
238
589
  }
239
- timerValueRef.current = 5;
240
- await getMembers();
241
- await readAllChannelAttributes();
242
- logger.log(LogSource.AgoraSDK, 'Log', 'RTM getMembers done');
243
590
  } catch (error) {
244
591
  logger.error(
245
592
  LogSource.AgoraSDK,
246
593
  'Log',
247
- 'RTM joinChannel failed..Trying again',
594
+ 'RTM subscribeChannel failed..Trying again',
595
+ {error},
248
596
  );
249
597
  setTimeout(async () => {
250
- timerValueRef.current = timerValueRef.current + timerValueRef.current;
251
- joinChannel();
598
+ // Cap the timer to prevent excessive delays (max 30 seconds)
599
+ timerValueRef.current = Math.min(timerValueRef.current * 2, 30);
600
+ subscribeChannel();
252
601
  }, timerValueRef.current * 1000);
253
602
  }
254
603
  };
255
604
 
256
- const updateRenderListState = (
257
- uid: number,
258
- data: Partial<ContentInterface>,
259
- ) => {
260
- dispatch({type: 'UpdateRenderList', value: [uid, data]});
261
- };
262
-
263
605
  const getMembers = async () => {
264
606
  try {
265
607
  logger.log(
266
608
  LogSource.AgoraSDK,
267
609
  'API',
268
- 'RTM getChannelMembersByID(getMembers) start',
610
+ 'RTM presence.getOnlineUsers(getMembers) start',
269
611
  );
270
- await engine.current
271
- .getChannelMembersBychannelId(rtcProps.channel)
272
- .then(async data => {
612
+ await engine.current.presence
613
+ .getOnlineUsers(rtcProps.channel, 1)
614
+ .then(async (data: GetOnlineUsersResponse) => {
273
615
  logger.log(
274
616
  LogSource.AgoraSDK,
275
617
  'API',
276
- 'RTM getChannelMembersByID data received',
618
+ 'RTM presence.getOnlineUsers data received',
277
619
  data,
278
620
  );
279
621
  await Promise.all(
280
- data.members.map(async (member: any) => {
281
- const backoffAttributes = backOff(
282
- async () => {
283
- logger.log(
284
- LogSource.AgoraSDK,
285
- 'API',
286
- `RTM fetching getUserAttributesByUid for member ${member.uid}`,
287
- );
288
- const attr = await engine.current.getUserAttributesByUid(
289
- member.uid,
290
- );
291
- if (!attr || !attr.attributes) {
292
- logger.log(
293
- LogSource.AgoraSDK,
294
- 'API',
295
- 'RTM attributes for member not found',
296
- );
297
- throw attr;
298
- }
299
- logger.log(
300
- LogSource.AgoraSDK,
301
- 'API',
302
- `RTM getUserAttributesByUid for member ${member.uid} received`,
303
- {
304
- attr,
305
- },
306
- );
307
- for (const key in attr.attributes) {
308
- if (
309
- attr.attributes.hasOwnProperty(key) &&
310
- attr.attributes[key]
311
- ) {
312
- return attr;
313
- } else {
314
- throw attr;
315
- }
316
- }
317
- },
318
- {
319
- retry: (e, idx) => {
320
- logger.debug(
321
- LogSource.AgoraSDK,
322
- 'Log',
323
- `[retrying] Attempt ${idx}. Fetching ${member.uid}'s name`,
324
- e,
325
- );
326
- return true;
327
- },
328
- },
329
- );
622
+ data.occupants?.map(async member => {
330
623
  try {
331
- const attr = await backoffAttributes;
332
- console.log('[user attributes]:', {attr});
333
- //RTC layer uid type is number. so doing the parseInt to convert to number
334
- //todo hari check android uid comparsion
335
- const uid = parseInt(member.uid);
336
- const screenUid = parseInt(attr?.attributes?.screenUid);
337
- //start - updating user data in rtc
338
- const userData = {
339
- screenUid: screenUid,
340
- //below thing for livestreaming
341
- type: uid === parseInt(RECORDING_BOT_UID) ? 'bot' : 'rtc',
342
- uid,
343
- offline: false,
344
- isHost: attr?.attributes?.isHost,
345
- lastMessageTimeStamp: 0,
346
- };
347
- updateRenderListState(uid, userData);
348
- //end- updating user data in rtc
624
+ const backoffAttributes =
625
+ await fetchUserAttributesWithBackoffRetry(member.userId);
349
626
 
350
- //start - updating screenshare data in rtc
351
- const screenShareUser = {
352
- type: UserType.ScreenShare,
353
- parentUid: uid,
354
- };
355
- updateRenderListState(screenUid, screenShareUser);
356
- //end - updating screenshare data in rtc
627
+ await processUserUidAttributes(
628
+ backoffAttributes,
629
+ member.userId,
630
+ );
357
631
  // setting screenshare data
358
632
  // name of the screenUid, isActive: false, (when the user starts screensharing it becomes true)
359
633
  // isActive to identify all active screenshare users in the call
360
- for (const [key, value] of Object.entries(attr?.attributes)) {
361
- if (hasJsonStructure(value as string)) {
362
- const data = {
363
- evt: key,
364
- value: value,
365
- };
366
- // TODOSUP: Add the data to queue, dont add same mulitple events, use set so as to not repeat events
367
- EventsQueue.enqueue({
368
- data: data,
369
- uid: member.uid,
370
- ts: timeNow(),
371
- });
634
+ backoffAttributes?.items?.forEach(item => {
635
+ try {
636
+ if (hasJsonStructure(item.value as string)) {
637
+ const data = {
638
+ evt: item.key, // Use item.key instead of key
639
+ value: item.value, // Use item.value instead of value
640
+ };
641
+ // TODOSUP: Add the data to queue, dont add same mulitple events, use set so as to not repeat events
642
+ EventsQueue.enqueue({
643
+ data: data,
644
+ uid: member.userId,
645
+ ts: timeNow(),
646
+ });
647
+ }
648
+ } catch (error) {
649
+ logger.error(
650
+ LogSource.AgoraSDK,
651
+ 'Log',
652
+ `RTM Failed to process user attribute item for ${
653
+ member.userId
654
+ }: ${JSON.stringify(item)}`,
655
+ {error},
656
+ );
657
+ // Continue processing other items
372
658
  }
373
- }
659
+ });
374
660
  } catch (e) {
375
661
  logger.error(
376
662
  LogSource.AgoraSDK,
377
663
  'Log',
378
- `Could not retrieve name of ${member.uid}`,
379
- e,
664
+ `RTM Could not retrieve name of ${member.userId}`,
665
+ {error: e},
380
666
  );
381
667
  }
382
668
  }),
@@ -390,7 +676,8 @@ const RtmConfigure = (props: any) => {
390
676
  timerValueRef.current = 5;
391
677
  } catch (error) {
392
678
  setTimeout(async () => {
393
- timerValueRef.current = timerValueRef.current + timerValueRef.current;
679
+ // Cap the timer to prevent excessive delays (max 30 seconds)
680
+ timerValueRef.current = Math.min(timerValueRef.current * 2, 30);
394
681
  await getMembers();
395
682
  }, timerValueRef.current * 1000);
396
683
  }
@@ -398,286 +685,169 @@ const RtmConfigure = (props: any) => {
398
685
 
399
686
  const readAllChannelAttributes = async () => {
400
687
  try {
401
- await engine.current
402
- .getChannelAttributes(rtcProps.channel)
403
- .then(async data => {
404
- for (const item of data) {
405
- const {key, value, lastUpdateTs, lastUpdateUserId} = item;
406
- if (hasJsonStructure(value as string)) {
407
- const evtData = {
408
- evt: key,
409
- value,
410
- };
411
- // TODOSUP: Add the data to queue, dont add same mulitple events, use set so as to not repeat events
412
- EventsQueue.enqueue({
413
- data: evtData,
414
- uid: lastUpdateUserId,
415
- ts: lastUpdateTs,
416
- });
688
+ await engine.current.storage
689
+ .getChannelMetadata(rtcProps.channel, 1)
690
+ .then(async (data: GetChannelMetadataResponse) => {
691
+ for (const item of data.items) {
692
+ try {
693
+ const {key, value, authorUserId, updateTs} = item;
694
+ if (hasJsonStructure(value as string)) {
695
+ const evtData = {
696
+ evt: key,
697
+ value,
698
+ };
699
+ // TODOSUP: Add the data to queue, dont add same mulitple events, use set so as to not repeat events
700
+ EventsQueue.enqueue({
701
+ data: evtData,
702
+ uid: authorUserId,
703
+ ts: updateTs,
704
+ });
705
+ }
706
+ } catch (error) {
707
+ logger.error(
708
+ LogSource.AgoraSDK,
709
+ 'Log',
710
+ `RTM Failed to process channel attribute item: ${JSON.stringify(
711
+ item,
712
+ )}`,
713
+ {error},
714
+ );
715
+ // Continue processing other items
417
716
  }
418
717
  }
419
718
  logger.log(
420
719
  LogSource.AgoraSDK,
421
720
  'API',
422
- 'RTM getChannelAttributes data received',
721
+ 'RTM storage.getChannelMetadata data received',
423
722
  data,
424
723
  );
425
724
  });
426
725
  timerValueRef.current = 5;
427
726
  } catch (error) {
428
727
  setTimeout(async () => {
429
- timerValueRef.current = timerValueRef.current + timerValueRef.current;
728
+ // Cap the timer to prevent excessive delays (max 30 seconds)
729
+ timerValueRef.current = Math.min(timerValueRef.current * 2, 30);
430
730
  await readAllChannelAttributes();
431
731
  }, timerValueRef.current * 1000);
432
732
  }
433
733
  };
434
734
 
435
- const init = async () => {
436
- //on sdk due to multiple re-render we are getting rtm error code 8
437
- //you are joining the same channel too frequently, exceeding the allowed rate of joining the same channel multiple times within a short period
438
- //so checking rtm connection state before proceed
439
- if (engine?.current?.client?.connectionState === 'CONNECTED') {
440
- return;
441
- }
442
- logger.log(LogSource.AgoraSDK, 'Log', 'RTM creating engine...');
443
- engine.current = RTMEngine.getInstance().engine;
444
- RTMEngine.getInstance();
445
- logger.log(LogSource.AgoraSDK, 'Log', 'RTM engine creation done');
735
+ const fetchUserAttributesWithBackoffRetry = async (
736
+ userId: string,
737
+ ): Promise<GetUserMetadataResponse> => {
738
+ return backOff(
739
+ async () => {
740
+ logger.log(
741
+ LogSource.AgoraSDK,
742
+ 'API',
743
+ `RTM fetching getUserMetadata for member ${userId}`,
744
+ );
446
745
 
447
- engine.current.on('connectionStateChanged', (evt: any) => {
448
- //console.log(evt);
449
- });
450
- engine.current.on('error', (evt: any) => {
451
- // console.log(evt);
452
- });
453
- engine.current.on('channelMemberJoined', (data: any) => {
454
- logger.log(LogSource.AgoraSDK, 'Event', 'channelMemberJoined', data);
455
- const backoffAttributes = backOff(
456
- async () => {
457
- logger.log(
458
- LogSource.AgoraSDK,
459
- 'API',
460
- `RTM fetching getUserAttributesByUid for member ${data.uid}`,
461
- );
462
- const attr = await engine.current.getUserAttributesByUid(data.uid);
463
- if (!attr || !attr.attributes) {
464
- logger.log(
465
- LogSource.AgoraSDK,
466
- 'API',
467
- 'RTM attributes for member not found',
468
- );
469
- throw attr;
470
- }
746
+ const attr: GetUserMetadataResponse =
747
+ await engine.current.storage.getUserMetadata({
748
+ userId: userId,
749
+ });
750
+
751
+ if (!attr || !attr.items) {
471
752
  logger.log(
472
753
  LogSource.AgoraSDK,
473
754
  'API',
474
- `RTM getUserAttributesByUid for member ${data.uid} received`,
475
- {
476
- attr,
477
- },
478
- );
479
- for (const key in attr.attributes) {
480
- if (attr.attributes.hasOwnProperty(key) && attr.attributes[key]) {
481
- return attr;
482
- } else {
483
- throw attr;
484
- }
485
- }
486
- },
487
- {
488
- retry: (e, idx) => {
489
- logger.debug(
490
- LogSource.AgoraSDK,
491
- 'Log',
492
- `[retrying] Attempt ${idx}. Fetching ${data.uid}'s name`,
493
- e,
494
- );
495
- return true;
496
- },
497
- },
498
- );
499
- async function getname() {
500
- try {
501
- const attr = await backoffAttributes;
502
- console.log('[user attributes]:', {attr});
503
- const uid = parseInt(data.uid);
504
- const screenUid = parseInt(attr?.attributes?.screenUid);
505
-
506
- //start - updating user data in rtc
507
- const userData = {
508
- screenUid: screenUid,
509
- //below thing for livestreaming
510
- type: uid === parseInt(RECORDING_BOT_UID) ? 'bot' : 'rtc',
511
- uid,
512
- offline: false,
513
- lastMessageTimeStamp: 0,
514
- isHost: attr?.attributes?.isHost,
515
- };
516
- updateRenderListState(uid, userData);
517
- //end- updating user data in rtc
518
-
519
- //start - updating screenshare data in rtc
520
- const screenShareUser = {
521
- type: UserType.ScreenShare,
522
- parentUid: uid,
523
- };
524
- updateRenderListState(screenUid, screenShareUser);
525
- //end - updating screenshare data in rtc
526
- } catch (e) {
527
- logger.error(
528
- LogSource.AgoraSDK,
529
- 'Event',
530
- `Failed to retrive name of ${data.uid}`,
531
- e,
755
+ 'RTM attributes for member not found',
532
756
  );
757
+ throw attr;
533
758
  }
534
- }
535
- getname();
536
- });
537
759
 
538
- engine.current.on('channelMemberLeft', (data: any) => {
539
- logger.debug(LogSource.AgoraSDK, 'Event', 'channelMemberLeft', data);
540
- // Chat of left user becomes undefined. So don't cleanup
541
- const uid = data?.uid ? parseInt(data?.uid) : undefined;
542
- if (!uid) return;
543
- SDKEvents.emit('_rtm-left', uid);
544
- // updating the rtc data
545
- updateRenderListState(uid, {
546
- offline: true,
547
- });
548
- });
760
+ logger.log(
761
+ LogSource.AgoraSDK,
762
+ 'API',
763
+ `RTM getUserMetadata for member ${userId} received`,
764
+ {attr},
765
+ );
549
766
 
550
- engine.current.addListener(
551
- 'ChannelAttributesUpdated',
552
- (attributeList: RtmChannelAttribute[]) => {
553
- try {
554
- attributeList.map((attribute: RtmChannelAttribute) => {
555
- const {key, value, lastUpdateTs, lastUpdateUserId} = attribute;
556
- const timestamp = getMessageTime(lastUpdateTs);
557
- const sender = Platform.OS
558
- ? get32BitUid(lastUpdateUserId)
559
- : parseInt(lastUpdateUserId);
560
- eventDispatcher(
561
- {
562
- evt: key,
563
- value,
564
- },
565
- sender,
566
- timestamp,
567
- );
568
- });
569
- } catch (error) {
570
- logger.error(
571
- LogSource.Events,
572
- 'CUSTOM_EVENTS',
573
- 'error while dispatching through eventDispatcher',
574
- error,
575
- );
767
+ if (attr.items && attr.items.length > 0) {
768
+ return attr;
769
+ } else {
770
+ throw attr;
576
771
  }
577
772
  },
773
+ {
774
+ retry: (e, idx) => {
775
+ logger.debug(
776
+ LogSource.AgoraSDK,
777
+ 'Log',
778
+ `RTM [retrying] Attempt ${idx}. Fetching ${userId}'s attributes`,
779
+ e,
780
+ );
781
+ return true;
782
+ },
783
+ },
578
784
  );
785
+ };
579
786
 
580
- engine.current.on('messageReceived', (evt: any) => {
581
- logger.debug(LogSource.Events, 'CUSTOM_EVENTS', 'messageReceived', evt);
582
- const {peerId, ts, text} = evt;
583
- const [err, msg] = safeJsonParse(text);
584
- if (err) {
585
- logger.error(
586
- LogSource.Events,
587
- 'CUSTOM_EVENTS',
588
- 'JSON payload incorrect, Error while parsing the payload',
589
- err,
590
- );
591
- }
592
-
593
- const timestamp = getMessageTime(ts);
787
+ const processUserUidAttributes = async (
788
+ attr: GetUserMetadataResponse,
789
+ userId: string,
790
+ ) => {
791
+ try {
792
+ console.log('[user attributes]:', {attr});
793
+ const uid = parseInt(userId, 10);
794
+ const screenUidItem = attr?.items?.find(item => item.key === 'screenUid');
795
+ const isHostItem = attr?.items?.find(item => item.key === 'isHost');
796
+ const screenUid = screenUidItem?.value
797
+ ? parseInt(screenUidItem.value, 10)
798
+ : undefined;
594
799
 
595
- const sender = isAndroid() ? get32BitUid(peerId) : parseInt(peerId);
800
+ //start - updating user data in rtc
801
+ const userData = {
802
+ screenUid: screenUid,
803
+ //below thing for livestreaming
804
+ type: uid === parseInt(RECORDING_BOT_UID, 10) ? 'bot' : 'rtc',
805
+ uid,
806
+ offline: false,
807
+ isHost: isHostItem?.value || false,
808
+ lastMessageTimeStamp: 0,
809
+ };
810
+ updateRenderListState(uid, userData);
811
+ //end- updating user data in rtc
596
812
 
597
- try {
598
- eventDispatcher(msg, sender, timestamp);
599
- } catch (error) {
600
- logger.error(
601
- LogSource.Events,
602
- 'CUSTOM_EVENTS',
603
- 'error while dispatching through eventDispatcher',
604
- err,
605
- );
813
+ //start - updating screenshare data in rtc
814
+ if (screenUid) {
815
+ const screenShareUser = {
816
+ type: UserType.ScreenShare,
817
+ parentUid: uid,
818
+ };
819
+ updateRenderListState(screenUid, screenShareUser);
606
820
  }
607
- });
608
-
609
- engine.current.on('channelMessageReceived', evt => {
610
- logger.debug(
611
- LogSource.Events,
612
- 'CUSTOM_EVENTS',
613
- 'channelMessageReceived',
614
- evt,
821
+ //end - updating screenshare data in rtc
822
+ } catch (e) {
823
+ logger.error(
824
+ LogSource.AgoraSDK,
825
+ 'Event',
826
+ `RTM Failed to process user data for ${userId}`,
827
+ {error: e},
615
828
  );
829
+ }
830
+ };
616
831
 
617
- const {uid, channelId, text, ts} = evt;
618
- //whiteboard upload
619
- if (uid == 1010101) {
620
- const [err, res] = safeJsonParse(text);
621
- if (err) {
622
- logger.error(
623
- LogSource.Events,
624
- 'CUSTOM_EVENTS',
625
- 'JSON payload incorrect, Error while parsing the payload',
626
- err,
627
- );
628
- }
629
-
630
- if (res?.data?.data?.images) {
631
- LocalEventEmitter.emit(
632
- LocalEventsEnum.WHITEBOARD_FILE_UPLOAD,
633
- res?.data?.data?.images,
634
- );
635
- }
636
- } else {
637
- const [err, msg] = safeJsonParse(text);
638
- if (err) {
639
- logger.error(
640
- LogSource.Events,
641
- 'CUSTOM_EVENTS',
642
- 'JSON payload incorrect, Error while parsing the payload',
643
- err,
644
- );
645
- }
646
-
647
- const timestamp = getMessageTime(ts);
648
-
649
- const sender = Platform.OS ? get32BitUid(uid) : parseInt(uid);
650
-
651
- if (channelId === rtcProps.channel) {
652
- try {
653
- eventDispatcher(msg, sender, timestamp);
654
- } catch (error) {
655
- logger.error(
656
- LogSource.Events,
657
- 'CUSTOM_EVENTS',
658
- 'error while dispatching through eventDispatcher',
659
- error,
660
- );
661
- }
662
- }
663
- }
664
- });
665
-
666
- await doLoginAndSetupRTM();
832
+ const updateRenderListState = (
833
+ uid: number,
834
+ data: Partial<ContentInterface>,
835
+ ) => {
836
+ dispatch({type: 'UpdateRenderList', value: [uid, data]});
667
837
  };
668
838
 
669
839
  const runQueuedEvents = async () => {
670
840
  try {
671
841
  while (!EventsQueue.isEmpty()) {
672
842
  const currEvt = EventsQueue.dequeue();
673
- await eventDispatcher(currEvt.data, currEvt.uid, currEvt.ts);
843
+ await eventDispatcher(currEvt.data, `${currEvt.uid}`, currEvt.ts);
674
844
  }
675
845
  } catch (error) {
676
846
  logger.error(
677
847
  LogSource.Events,
678
848
  'CUSTOM_EVENTS',
679
849
  'error while running queue events',
680
- error,
850
+ {error},
681
851
  );
682
852
  }
683
853
  };
@@ -686,11 +856,13 @@ const RtmConfigure = (props: any) => {
686
856
  data: {
687
857
  evt: string;
688
858
  value: string;
859
+ feat?: string;
860
+ etyp?: string;
689
861
  },
690
862
  sender: string,
691
863
  ts: number,
692
864
  ) => {
693
- console.debug(
865
+ console.log(
694
866
  LogSource.Events,
695
867
  'CUSTOM_EVENTS',
696
868
  'inside eventDispatcher ',
@@ -698,10 +870,10 @@ const RtmConfigure = (props: any) => {
698
870
  );
699
871
 
700
872
  let evt = '',
701
- value = {};
873
+ value = '';
702
874
 
703
- if (data.feat === 'WAITING_ROOM') {
704
- if (data.etyp === 'REQUEST') {
875
+ if (data?.feat === 'WAITING_ROOM') {
876
+ if (data?.etyp === 'REQUEST') {
705
877
  const outputData = {
706
878
  evt: `${data.feat}_${data.etyp}`,
707
879
  payload: JSON.stringify({
@@ -715,7 +887,7 @@ const RtmConfigure = (props: any) => {
715
887
  evt = data.feat + '_' + data.etyp; //rename if client side RTM meessage is to be sent for approval
716
888
  value = formattedData;
717
889
  }
718
- if (data.etyp === 'RESPONSE') {
890
+ if (data?.etyp === 'RESPONSE') {
719
891
  const outputData = {
720
892
  evt: `${data.feat}_${data.etyp}`,
721
893
  payload: JSON.stringify({
@@ -756,32 +928,65 @@ const RtmConfigure = (props: any) => {
756
928
  }
757
929
 
758
930
  try {
759
- const {payload, persistLevel, source} = JSON.parse(value);
931
+ let parsedValue;
932
+ try {
933
+ parsedValue = typeof value === 'string' ? JSON.parse(value) : value;
934
+ } catch (error) {
935
+ logger.error(
936
+ LogSource.Events,
937
+ 'CUSTOM_EVENTS',
938
+ 'RTM Failed to parse event value in event dispatcher:',
939
+ {error},
940
+ );
941
+ return;
942
+ }
943
+ const {payload, persistLevel, source} = parsedValue;
760
944
  // Step 1: Set local attributes
761
945
  if (persistLevel === PersistanceLevel.Session) {
762
946
  const rtmAttribute = {key: evt, value: value};
763
- await engine.current.addOrUpdateLocalUserAttributes([rtmAttribute]);
947
+ const options: SetOrUpdateUserMetadataOptions = {
948
+ userId: `${localUid}`,
949
+ };
950
+ await engine.current.storage.setUserMetadata(
951
+ {
952
+ items: [rtmAttribute],
953
+ },
954
+ options,
955
+ );
764
956
  }
765
957
  // Step 2: Emit the event
766
- console.debug(LogSource.Events, 'CUSTOM_EVENTS', 'emiting event..: ');
958
+ console.log(LogSource.Events, 'CUSTOM_EVENTS', 'emiting event..: ');
767
959
  EventUtils.emitEvent(evt, source, {payload, persistLevel, sender, ts});
768
960
  // Because async gets evaluated in a different order when in an sdk
769
961
  if (evt === 'name') {
770
- setTimeout(() => {
962
+ // 1. Cancel existing timeout for this sender
963
+ if (eventTimeouts.has(sender)) {
964
+ clearTimeout(eventTimeouts.get(sender)!);
965
+ }
966
+ // 2. Create new timeout with tracking
967
+ const timeout = setTimeout(() => {
968
+ // 3. Guard against unmounted component
969
+ if (!isRTMMounted.current) {
970
+ return;
971
+ }
771
972
  EventUtils.emitEvent(evt, source, {
772
973
  payload,
773
974
  persistLevel,
774
975
  sender,
775
976
  ts,
776
977
  });
978
+ // 4. Clean up after execution
979
+ eventTimeouts.delete(sender);
777
980
  }, 200);
981
+ // 5. Track the timeout for cleanup
982
+ eventTimeouts.set(sender, timeout);
778
983
  }
779
984
  } catch (error) {
780
985
  console.error(
781
986
  LogSource.Events,
782
987
  'CUSTOM_EVENTS',
783
988
  'error while emiting event:',
784
- error,
989
+ {error},
785
990
  );
786
991
  }
787
992
  };
@@ -790,45 +995,58 @@ const RtmConfigure = (props: any) => {
790
995
  if (!callActive) {
791
996
  return;
792
997
  }
998
+ // Destroy and clean up RTM state
793
999
  await RTMEngine.getInstance().destroy();
1000
+ // Set the engine as null
1001
+ engine.current = null;
794
1002
  logger.log(LogSource.AgoraSDK, 'API', 'RTM destroy done');
795
1003
  if (isIOS() || isAndroid()) {
796
1004
  EventUtils.clear();
797
1005
  }
798
1006
  setHasUserJoinedRTM(false);
1007
+ setIsInitialQueueCompleted(false);
799
1008
  logger.debug(LogSource.AgoraSDK, 'Log', 'RTM cleanup done');
800
1009
  };
801
1010
 
802
1011
  useAsyncEffect(async () => {
803
1012
  //waiting room attendee -> rtm login will happen on page load
804
- if ($config.ENABLE_WAITING_ROOM) {
805
- //attendee
806
- //for waiting room attendee rtm login will happen on mount
807
- if (!isHost && !callActive) {
808
- await init();
809
- }
810
- //host
811
- if (
812
- isHost &&
813
- ($config.AUTO_CONNECT_RTM || (!$config.AUTO_CONNECT_RTM && callActive))
814
- ) {
815
- await init();
816
- }
817
- } else {
818
- //non waiting room case
819
- //host and attendee
820
- if (
821
- $config.AUTO_CONNECT_RTM ||
822
- (!$config.AUTO_CONNECT_RTM && callActive)
823
- ) {
824
- await init();
1013
+ try {
1014
+ if ($config.ENABLE_WAITING_ROOM) {
1015
+ //attendee
1016
+ //for waiting room attendee rtm login will happen on mount
1017
+ if (!isHost && !callActive) {
1018
+ await init(localUid);
1019
+ }
1020
+ //host
1021
+ if (
1022
+ isHost &&
1023
+ ($config.AUTO_CONNECT_RTM ||
1024
+ (!$config.AUTO_CONNECT_RTM && callActive))
1025
+ ) {
1026
+ await init(localUid);
1027
+ }
1028
+ } else {
1029
+ //non waiting room case
1030
+ //host and attendee
1031
+ if (
1032
+ $config.AUTO_CONNECT_RTM ||
1033
+ (!$config.AUTO_CONNECT_RTM && callActive)
1034
+ ) {
1035
+ await init(localUid);
1036
+ }
825
1037
  }
1038
+ } catch (error) {
1039
+ logger.error(LogSource.AgoraSDK, 'Log', 'RTM init failed', {error});
826
1040
  }
827
1041
  return async () => {
1042
+ logger.log(
1043
+ LogSource.AgoraSDK,
1044
+ 'Log',
1045
+ 'RTM unmounting calling end(destroy) ',
1046
+ );
828
1047
  await end();
829
1048
  };
830
- // eslint-disable-next-line react-hooks/exhaustive-deps
831
- }, [rtcProps.channel, rtcProps.appId, callActive]);
1049
+ }, [rtcProps.channel, rtcProps.appId, callActive, localUid]);
832
1050
 
833
1051
  return (
834
1052
  <ChatContext.Provider