agora-appbuilder-core 4.0.24 → 4.0.25-beta-2

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.
Files changed (79) hide show
  1. package/package.json +1 -1
  2. package/template/agora-rn-uikit/.eslintrc.js +0 -5
  3. package/template/agora-rn-uikit/package-lock.json +6116 -23528
  4. package/template/agora-rn-uikit/package.json +14 -14
  5. package/template/agora-rn-uikit/src/AgoraUIKit.tsx +6 -6
  6. package/template/agora-rn-uikit/src/Contexts/LocalUserContext.tsx +6 -6
  7. package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +40 -79
  8. package/template/agora-rn-uikit/src/Contexts/RenderContext.tsx +10 -0
  9. package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +17 -26
  10. package/template/agora-rn-uikit/src/Controls/BtnTemplate.tsx +0 -1
  11. package/template/agora-rn-uikit/src/Controls/Icons.ts +0 -51
  12. package/template/agora-rn-uikit/src/Controls/Local/EndCall.tsx +4 -6
  13. package/template/agora-rn-uikit/src/Controls/Local/FullScreen.tsx +1 -3
  14. package/template/agora-rn-uikit/src/Controls/Local/LocalAudioMute.tsx +2 -5
  15. package/template/agora-rn-uikit/src/Controls/Local/LocalVideoMute.tsx +2 -5
  16. package/template/agora-rn-uikit/src/Controls/Local/Recording.tsx +2 -0
  17. package/template/agora-rn-uikit/src/Controls/Local/SwitchCamera.tsx +6 -7
  18. package/template/agora-rn-uikit/src/Controls/LocalControls.tsx +5 -5
  19. package/template/agora-rn-uikit/src/Controls/Remote/RemoteAudioMute.tsx +4 -5
  20. package/template/agora-rn-uikit/src/Controls/Remote/RemoteSwap.tsx +1 -3
  21. package/template/agora-rn-uikit/src/Controls/Remote/RemoteVideoMute.tsx +4 -5
  22. package/template/agora-rn-uikit/src/Controls/RemoteControls.tsx +2 -2
  23. package/template/agora-rn-uikit/src/Reducer/ActiveSpeakerDetected.ts +11 -0
  24. package/template/agora-rn-uikit/src/Reducer/LocalMuteAudio.ts +7 -8
  25. package/template/agora-rn-uikit/src/Reducer/LocalMuteVideo.ts +7 -8
  26. package/template/agora-rn-uikit/src/Reducer/LocalPermissionState.ts +7 -6
  27. package/template/agora-rn-uikit/src/Reducer/RemoteAudioStateChanged.ts +8 -7
  28. package/template/agora-rn-uikit/src/Reducer/RemoteVideoStateChanged.ts +9 -9
  29. package/template/agora-rn-uikit/src/Reducer/UpdateDualStreamMode.ts +8 -7
  30. package/template/agora-rn-uikit/src/Reducer/UserJoined.ts +13 -14
  31. package/template/agora-rn-uikit/src/Reducer/UserMuteRemoteAudio.ts +7 -6
  32. package/template/agora-rn-uikit/src/Reducer/UserMuteRemoteVideo.ts +7 -6
  33. package/template/agora-rn-uikit/src/Reducer/UserOffline.ts +6 -5
  34. package/template/agora-rn-uikit/src/Reducer/UserPin.ts +3 -20
  35. package/template/agora-rn-uikit/src/Reducer/index.ts +1 -2
  36. package/template/agora-rn-uikit/src/Rtc/Create.tsx +125 -122
  37. package/template/agora-rn-uikit/src/Rtc/Join.tsx +27 -46
  38. package/template/agora-rn-uikit/src/RtcConfigure.tsx +77 -175
  39. package/template/agora-rn-uikit/src/Utils/quality.tsx +0 -8
  40. package/template/agora-rn-uikit/src/Views/MaxVideoView.native.tsx +12 -56
  41. package/template/agora-rn-uikit/src/Views/MaxVideoView.tsx +17 -47
  42. package/template/agora-rn-uikit/src/Views/MinVideoView.tsx +11 -19
  43. package/template/agora-rn-uikit/src/index.ts +9 -16
  44. package/template/customization-api/typeDefinition.ts +1 -4
  45. package/template/customization-api/utils.ts +1 -0
  46. package/template/defaultConfig.js +2 -0
  47. package/template/global.d.ts +2 -0
  48. package/template/src/AppRoutes.tsx +1 -7
  49. package/template/src/auth/AuthProvider.tsx +70 -24
  50. package/template/src/auth/useIDPAuth.electron.tsx +59 -2
  51. package/template/src/auth/useIDPAuth.native.tsx +59 -2
  52. package/template/src/auth/useIDPAuth.tsx +60 -2
  53. package/template/src/auth/useTokenAuth.tsx +66 -4
  54. package/template/src/components/DeviceConfigure.tsx +74 -68
  55. package/template/src/components/GraphQLProvider.tsx +0 -1
  56. package/template/src/components/contexts/WaitingRoomContext.tsx +11 -4
  57. package/template/src/components/recording-bot/RecordingBotRoute.tsx +1 -1
  58. package/template/src/components/useUserPreference.tsx +56 -9
  59. package/template/src/components/whiteboard/WhiteboardConfigure.tsx +4 -1
  60. package/template/src/components/whiteboard/WhiteboardToolBox.tsx +185 -24
  61. package/template/src/components/whiteboard/WhiteboardWidget.tsx +47 -6
  62. package/template/src/logger/AppBuilderLogger.tsx +94 -36
  63. package/template/src/logger/transports/customer-transport.ts +7 -3
  64. package/template/src/pages/VideoCall.tsx +3 -5
  65. package/template/src/subComponents/SelectDevice.tsx +14 -52
  66. package/template/src/subComponents/caption/useSTTAPI.tsx +39 -3
  67. package/template/src/subComponents/recording/useRecording.tsx +69 -20
  68. package/template/src/subComponents/waiting-rooms/useWaitingRoomAPI.ts +30 -8
  69. package/template/src/utils/common.tsx +6 -1
  70. package/template/src/utils/useCreateRoom.ts +21 -2
  71. package/template/src/utils/useFindActiveSpeaker.ts +12 -5
  72. package/template/src/utils/useGetMeetingPhrase.ts +44 -1
  73. package/template/src/utils/useJoinRoom.ts +29 -2
  74. package/template/src/utils/useMutePSTN.ts +29 -0
  75. package/template/agora-rn-uikit/src/Contexts/ContentContext.tsx +0 -10
  76. package/template/agora-rn-uikit/src/Contexts/DispatchContext.tsx +0 -22
  77. package/template/agora-rn-uikit/src/Reducer/ActiveSpeaker.ts +0 -30
  78. package/template/agora-rn-uikit/src/Reducer/UserSecondaryPin.ts +0 -23
  79. package/template/agora-rn-uikit/src/Utils/isBotUser.ts +0 -15
@@ -39,17 +39,42 @@ const useTokenAuth = () => {
39
39
 
40
40
  const getRefreshToken = async () => {
41
41
  if (store?.token) {
42
+ const requestId = getUniqueID();
43
+ const startReqTs = Date.now();
44
+ logger.log(
45
+ LogSource.NetworkRest,
46
+ 'token_refresh',
47
+ 'API token_refresh Trying to refresh the token',
48
+ {
49
+ requestId: requestId,
50
+ startReqTs,
51
+ },
52
+ );
42
53
  await fetch(`${$config.BACKEND_ENDPOINT}/v1/token/refresh`, {
43
54
  method: 'POST',
44
55
  headers: {
45
56
  'Content-Type': 'application/json',
46
57
  authorization: store?.token ? `Bearer ${store.token}` : '',
47
58
  'X-Platform-ID': getPlatformId(),
48
- 'X-Request-Id': getUniqueID(),
59
+ 'X-Request-Id': requestId,
49
60
  },
50
61
  })
51
62
  .then(response => response.json())
52
63
  .then(data => {
64
+ const endReqTs = Date.now();
65
+ const latency = endReqTs - startReqTs;
66
+ logger.log(
67
+ LogSource.NetworkRest,
68
+ 'token_refresh',
69
+ 'API token_refresh successfully done',
70
+ {
71
+ requestId,
72
+ responseData: data,
73
+ startReqTs,
74
+ endReqTs,
75
+ latency,
76
+ },
77
+ );
53
78
  if (data?.token) {
54
79
  updateToken(data.token);
55
80
  if (isSDK()) {
@@ -167,6 +192,17 @@ const useTokenAuth = () => {
167
192
  const tokenLogout = async (cookieLogout: boolean = false) => {
168
193
  return new Promise((resolve, reject) => {
169
194
  try {
195
+ const startReqTs = Date.now();
196
+ const requestId = getUniqueID();
197
+ logger.log(
198
+ LogSource.NetworkRest,
199
+ 'idp_logout',
200
+ 'Trying to call v1 logout',
201
+ {
202
+ startReqTs,
203
+ ...(cookieLogout ? {} : {requestId}),
204
+ },
205
+ );
170
206
  fetch(
171
207
  `${$config.BACKEND_ENDPOINT}/v1/logout`,
172
208
  cookieLogout
@@ -176,16 +212,42 @@ const useTokenAuth = () => {
176
212
  authorization: tokenRef.current
177
213
  ? `Bearer ${tokenRef.current}`
178
214
  : '',
179
- 'X-Request-Id': getUniqueID(),
215
+ 'X-Request-Id': requestId,
180
216
  },
181
217
  },
182
218
  )
183
219
  .then(response => response.text())
184
- .then(_ => {
220
+ .then(res => {
221
+ const endReqTs = Date.now();
222
+ logger.log(
223
+ LogSource.NetworkRest,
224
+ 'idp_logout',
225
+ 'API V1 logout called successfully',
226
+ {
227
+ responseData: res,
228
+ startReqTs,
229
+ endReqTs,
230
+ latency: endReqTs - startReqTs,
231
+ ...(cookieLogout ? {} : {requestId}),
232
+ },
233
+ );
185
234
  resolve(true);
186
235
  updateToken(null);
187
236
  })
188
- .catch(_ => {
237
+ .catch(err => {
238
+ const endReqTs = Date.now();
239
+ logger.error(
240
+ LogSource.NetworkRest,
241
+ 'idp_logout',
242
+ 'Error API V1 logout called successfully',
243
+ {
244
+ error: err,
245
+ startReqTs,
246
+ endReqTs,
247
+ latency: endReqTs - startReqTs,
248
+ ...(cookieLogout ? {} : {requestId}),
249
+ },
250
+ );
189
251
  reject(false);
190
252
  });
191
253
  } catch (error) {
@@ -80,6 +80,8 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
80
80
  const speakerSelectInProgress = useRef(false);
81
81
  const speakerSelectQueue = useRef([]);
82
82
 
83
+ const muteRefreshDeviceList = useRef(false);
84
+
83
85
  const {primaryColor} = useContext(ColorContext);
84
86
 
85
87
  const {store, setStore} = useContext(StorageContext);
@@ -160,49 +162,52 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
160
162
  const {localStream} = RtcEngineUnsafe;
161
163
 
162
164
  const refreshDeviceList = useCallback(async (noEmitLog?: boolean) => {
163
- let updatedDeviceList: MediaDeviceInfo[];
164
- await RtcEngineUnsafe.getDevices(function (devices: deviceInfo[]) {
165
- !noEmitLog &&
166
- console.debug(
167
- LogSource.Internals,
168
- 'DEVICE_CONFIGURE',
169
- 'Fetching all devices: ',
170
- devices,
171
- );
172
- /**
173
- * Some browsers list the same microphone twice with different Id's,
174
- * their group Id's match as they are the same physical device.
175
- * deviceId == default is an oddity in chrome which stores the user
176
- * preference
177
- */
178
- /**
179
- * 1. Fetch devices and filter so the deviceId with empty
180
- * values are exluded
181
- * 2. Store only unique devices with unique groupIds
182
- */
183
-
184
- updatedDeviceList = devices.filter(
185
- (device: deviceInfo) =>
186
- // device?.deviceId !== 'default' &&
187
- device?.deviceId !== '' &&
188
- (device.kind == 'audioinput' ||
189
- device.kind == 'videoinput' ||
190
- device.kind == 'audiooutput'),
191
- );
192
-
193
- !noEmitLog &&
194
- console.debug(
195
- LogSource.Internals,
196
- 'DEVICE_CONFIGURE',
197
- 'Setting unique devices',
198
- updatedDeviceList,
165
+ if (!muteRefreshDeviceList.current) {
166
+ muteRefreshDeviceList.current = true;
167
+ let updatedDeviceList: MediaDeviceInfo[];
168
+ await RtcEngineUnsafe.getDevices(function (devices: deviceInfo[]) {
169
+ !noEmitLog &&
170
+ logger.log(
171
+ LogSource.Internals,
172
+ 'DEVICE_CONFIGURE',
173
+ 'Fetching all devices: ',
174
+ devices,
175
+ );
176
+ /**
177
+ * Some browsers list the same microphone twice with different Id's,
178
+ * their group Id's match as they are the same physical device.
179
+ * deviceId == default is an oddity in chrome which stores the user
180
+ * preference
181
+ */
182
+ /**
183
+ * 1. Fetch devices and filter so the deviceId with empty
184
+ * values are exluded
185
+ * 2. Store only unique devices with unique groupIds
186
+ */
187
+
188
+ updatedDeviceList = devices.filter(
189
+ (device: deviceInfo) =>
190
+ // device?.deviceId !== 'default' &&
191
+ device?.deviceId !== '' &&
192
+ (device.kind == 'audioinput' ||
193
+ device.kind == 'videoinput' ||
194
+ device.kind == 'audiooutput'),
199
195
  );
200
- if (updatedDeviceList.length > 0) {
201
- setDeviceList(updatedDeviceList);
202
- }
203
- });
204
196
 
205
- return updatedDeviceList;
197
+ !noEmitLog &&
198
+ logger.log(
199
+ LogSource.Internals,
200
+ 'DEVICE_CONFIGURE',
201
+ 'Setting unique devices',
202
+ updatedDeviceList,
203
+ );
204
+ if (updatedDeviceList.length > 0) {
205
+ setDeviceList(updatedDeviceList);
206
+ }
207
+ });
208
+ muteRefreshDeviceList.current = false;
209
+ return updatedDeviceList;
210
+ }
206
211
  }, []);
207
212
 
208
213
  const getAgoraTrackDeviceId = (type: 'audio' | 'video') => {
@@ -224,20 +229,22 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
224
229
  RtcEngineUnsafe.audioDeviceId
225
230
  : //@ts-ignore
226
231
  RtcEngineUnsafe.videoDeviceId;
227
- console.debug(
228
- LogSource.Internals,
229
- 'DEVICE_CONFIGURE',
230
- `Agora ${type} Engine is using ${currentDevice}`,
231
- );
232
+ currentDevice &&
233
+ logger.log(
234
+ LogSource.Internals,
235
+ 'DEVICE_CONFIGURE',
236
+ `Agora ${type} Engine is using ${currentDevice}`,
237
+ );
232
238
  } else {
233
239
  currentDevice = localStream[type]
234
240
  ?.getMediaStreamTrack()
235
241
  .getSettings().deviceId;
236
- console.debug(
237
- LogSource.Internals,
238
- 'DEVICE_CONFIGURE',
239
- `Agora ${type} Track is using ${currentDevice}`,
240
- );
242
+ currentDevice &&
243
+ logger.log(
244
+ LogSource.Internals,
245
+ 'DEVICE_CONFIGURE',
246
+ `Agora ${type} Track is using ${currentDevice}`,
247
+ );
241
248
  }
242
249
  return currentDevice ?? '';
243
250
  };
@@ -249,10 +256,10 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
249
256
  * truth.
250
257
  */
251
258
  const syncSelectedDeviceUi = (kind?: deviceKind) => {
252
- console.debug(
259
+ logger.log(
253
260
  LogSource.Internals,
254
261
  'DEVICE_CONFIGURE',
255
- 'Refreshing',
262
+ 'Refreshing UI for selected device',
256
263
  kind ?? 'all',
257
264
  );
258
265
  switch (kind) {
@@ -388,7 +395,7 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
388
395
  let count = 0;
389
396
  const interval = setInterval(() => {
390
397
  count = count + 1;
391
- refreshDeviceList(count % 10 !== 0);
398
+ refreshDeviceList(true);
392
399
  }, 2000);
393
400
  return () => {
394
401
  clearInterval(interval);
@@ -489,12 +496,6 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
489
496
  },
490
497
  }[kind];
491
498
 
492
- logger.debug(
493
- LogSource.Internals,
494
- 'DEVICE_CONFIGURE',
495
- `${logTag} ${deviceLogTag} Device list populated but none selected`,
496
- );
497
-
498
499
  if (sdkDevice?.deviceId && currentDevice) {
499
500
  if (checkDeviceExists(sdkDevice.deviceId, deviceList)) {
500
501
  applySdkDeviceChangeRequest(kind);
@@ -507,7 +508,7 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
507
508
  currentDevice !== storedDevice &&
508
509
  checkDeviceExists(storedDevice, deviceList)
509
510
  ) {
510
- logger.debug(
511
+ logger.log(
511
512
  LogSource.Internals,
512
513
  'DEVICE_CONFIGURE',
513
514
  `${logTag} ${deviceLogTag} Setting to active id ${storedDevice}`,
@@ -536,12 +537,12 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
536
537
  deviceList.length === 0 ||
537
538
  deviceList.find((device: MediaDeviceInfo) => device.label === '')
538
539
  ) {
539
- logger.debug(
540
+ logger.log(
540
541
  LogSource.Internals,
541
542
  'DEVICE_CONFIGURE',
542
543
  `${logTag} Empty device list`,
543
544
  );
544
- refreshDeviceList();
545
+ refreshDeviceList(false);
545
546
  }
546
547
  }, [rtc, store, deviceList]);
547
548
 
@@ -549,7 +550,7 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
549
550
  // Extracted devicelist because we want to perform fallback with
550
551
  // the most current version.
551
552
  const previousDeviceList = deviceList;
552
- const updatedDeviceList = await refreshDeviceList();
553
+ const updatedDeviceList = await refreshDeviceList(false);
553
554
  const changedDevice = changedDeviceData.device;
554
555
 
555
556
  const {logTag, currentDevice, setCurrentDevice} = {
@@ -570,7 +571,7 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
570
571
  },
571
572
  }[changedDevice.kind];
572
573
 
573
- logger.debug(
574
+ logger.log(
574
575
  LogSource.Internals,
575
576
  'DEVICE_CONFIGURE',
576
577
  `${logTag}`,
@@ -699,7 +700,7 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
699
700
  },
700
701
  }[kind];
701
702
 
702
- logger.debug(
703
+ logger.log(
703
704
  LogSource.Internals,
704
705
  'DEVICE_CONFIGURE',
705
706
  `${logtag} ${kind} setting to ${deviceId}`,
@@ -718,7 +719,7 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
718
719
  return new Promise<void>((res, rej) => {
719
720
  if (mutexRef.current) {
720
721
  const e = new Error(logtag + ' Change already in progress');
721
- logger.debug(
722
+ logger.error(
722
723
  LogSource.Internals,
723
724
  'DEVICE_CONFIGURE',
724
725
  `${logtag} Error setting ${kind}`,
@@ -739,7 +740,12 @@ const DeviceConfigure: React.FC<Props> = (props: any) => {
739
740
  },
740
741
  (e: any) => {
741
742
  mutexRef.current = false;
742
- console.error('DeviceConfigure:', logtag, 'Error setting', kind, e);
743
+ logger.error(
744
+ LogSource.Internals,
745
+ 'DEVICE_CONFIGURE',
746
+ `${logtag} Error setting ${kind}`,
747
+ {error: e},
748
+ );
743
749
  rej(e);
744
750
  handleQueue();
745
751
  },
@@ -54,7 +54,6 @@ const authLink = (token: string) => {
54
54
  ...headers,
55
55
  'X-Project-ID': $config.PROJECT_ID,
56
56
  'X-Platform-ID': 'turnkey_web',
57
- 'X-REQUEST-ID': getUniqueID(),
58
57
  ...(token && {
59
58
  authorization: token ? `Bearer ${token}` : '',
60
59
  }),
@@ -30,10 +30,17 @@ const WaitingRoomProvider = ({children}) => {
30
30
  v?.type === 'rtc' && !v.offline && v?.isInWaitingRoom === true,
31
31
  ),
32
32
  ).map(Number);
33
- logger.debug(LogSource.Internals, 'WAITING_ROOM', 'users in waiting room', {
34
- uids,
35
- defaultContent,
36
- });
33
+ if ($config.ENABLE_WAITING_ROOM) {
34
+ logger.debug(
35
+ LogSource.Internals,
36
+ 'WAITING_ROOM',
37
+ 'users in waiting room',
38
+ {
39
+ uids,
40
+ defaultContent,
41
+ },
42
+ );
43
+ }
37
44
 
38
45
  setwaitingRoomUids(uids);
39
46
  }, [defaultContent]);
@@ -26,7 +26,7 @@ const RecordingBotRoute: React.FC<RecordingBotRouteProps> = props => {
26
26
  const [ready, setReady] = useState(false);
27
27
  const {recordingBotToken} = useIsRecordingBot();
28
28
  useEffect(() => {
29
- logger.debug(
29
+ logger.log(
30
30
  LogSource.Internals,
31
31
  'RECORDING',
32
32
  'Inside the bot route wrapper',
@@ -32,6 +32,7 @@ import {
32
32
  import {useLanguage} from '../language/useLanguage';
33
33
  import {useScreenContext} from '../components/contexts/ScreenShareContext';
34
34
  import {LogSource, logger} from '../logger/AppBuilderLogger';
35
+ import getUniqueID from '../utils/getUniqueID';
35
36
 
36
37
  interface UserPreferenceContextInterface {
37
38
  displayName: string;
@@ -99,25 +100,71 @@ const UserPreferenceProvider = (props: {children: React.ReactNode}) => {
99
100
 
100
101
  const saveName = (name: string) => {
101
102
  if (name && name?.trim() !== '') {
103
+ const requestId = getUniqueID();
104
+ const startReqTs = Date.now();
102
105
  try {
106
+ logger.log(
107
+ LogSource.Internals,
108
+ 'NAME',
109
+ 'Trying to save the display name',
110
+ {
111
+ requestId,
112
+ startReqTs,
113
+ },
114
+ );
103
115
  updateUserName({
116
+ context: {
117
+ headers: {
118
+ 'X-Request-Id': requestId,
119
+ },
120
+ },
104
121
  variables: {
105
122
  name,
106
123
  },
107
- }).catch(error => {
108
- logger.error(
109
- LogSource.Internals,
110
- 'NAME',
111
- 'ERROR, could not save the name',
112
- error,
113
- );
114
- });
124
+ })
125
+ .then(res => {
126
+ const endReqTs = Date.now();
127
+ logger.log(
128
+ LogSource.Internals,
129
+ 'NAME',
130
+ 'name updated successfully',
131
+ {
132
+ responseData: res,
133
+ startReqTs,
134
+ endReqTs,
135
+ latency: endReqTs - startReqTs,
136
+ requestId,
137
+ },
138
+ );
139
+ })
140
+ .catch(error => {
141
+ const endReqTs = Date.now();
142
+ logger.error(
143
+ LogSource.Internals,
144
+ 'NAME',
145
+ 'ERROR, could not save the name',
146
+ {
147
+ error,
148
+ startReqTs,
149
+ endReqTs,
150
+ latency: endReqTs - startReqTs,
151
+ requestId,
152
+ },
153
+ );
154
+ });
115
155
  } catch (error) {
156
+ const endReqTs = Date.now();
116
157
  logger.error(
117
158
  LogSource.Internals,
118
159
  'NAME',
119
160
  'ERROR, could not save the name',
120
- error,
161
+ {
162
+ error,
163
+ startReqTs,
164
+ endReqTs,
165
+ latency: endReqTs - startReqTs,
166
+ requestId,
167
+ },
121
168
  );
122
169
  }
123
170
  }
@@ -420,7 +420,10 @@ const WhiteboardConfigure: React.FC<WhiteboardPropsInterface> = props => {
420
420
  } else if (whiteboardActive) {
421
421
  join();
422
422
  } else {
423
- if (whiteboardRoom.current) {
423
+ if (
424
+ whiteboardRoom.current &&
425
+ Object.keys(whiteboardRoom.current)?.length
426
+ ) {
424
427
  leave();
425
428
  }
426
429
  }