agora-appbuilder-core 4.1.8 → 4.1.9-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.
@@ -10,57 +10,71 @@
10
10
  *********************************************
11
11
  */
12
12
 
13
- import RtmEngine from 'agora-react-native-rtm';
13
+ import {
14
+ createAgoraRtmClient,
15
+ RtmConfig,
16
+ type RTMClient,
17
+ } from 'agora-react-native-rtm';
14
18
  import {isAndroid, isIOS} from '../utils/common';
15
19
 
16
20
  class RTMEngine {
17
- engine!: RtmEngine;
21
+ private _engine?: RTMClient;
18
22
  private localUID: string = '';
19
23
  private channelId: string = '';
20
24
 
21
25
  private static _instance: RTMEngine | null = null;
22
26
 
27
+ private constructor() {
28
+ if (RTMEngine._instance) {
29
+ return RTMEngine._instance;
30
+ }
31
+ RTMEngine._instance = this;
32
+ return RTMEngine._instance;
33
+ }
34
+
23
35
  public static getInstance() {
36
+ // We are only creating the instance but not creating the rtm client yet
24
37
  if (!RTMEngine._instance) {
25
- return new RTMEngine();
38
+ RTMEngine._instance = new RTMEngine();
26
39
  }
27
40
  return RTMEngine._instance;
28
41
  }
29
42
 
30
- private async createClientInstance() {
31
- await this.engine.createClient($config.APP_ID);
32
- }
43
+ setLocalUID(localUID: string | number) {
44
+ if (localUID === null || localUID === undefined) {
45
+ throw new Error('setLocalUID: localUID cannot be null or undefined');
46
+ }
33
47
 
34
- private async destroyClientInstance() {
35
- await this.engine.logout();
36
- if (isIOS() || isAndroid()) {
37
- await this.engine.destroyClient();
48
+ const newUID = String(localUID);
49
+ if (newUID.trim() === '') {
50
+ throw new Error(
51
+ 'setLocalUID: localUID cannot be empty after string conversion',
52
+ );
38
53
  }
39
- }
40
54
 
41
- private constructor() {
42
- if (RTMEngine._instance) {
43
- return RTMEngine._instance;
55
+ // If UID is changing and we have an existing engine, throw error
56
+ if (this._engine && this.localUID !== newUID) {
57
+ throw new Error(
58
+ `RTMEngine: Cannot change UID from '${this.localUID}' to '${newUID}' while engine is active. ` +
59
+ `Please call destroy() first, then setLocalUID() with the new UID.`,
60
+ );
44
61
  }
45
- RTMEngine._instance = this;
46
- this.engine = new RtmEngine();
47
- this.localUID = '';
48
- this.channelId = '';
49
- this.createClientInstance();
50
62
 
51
- return RTMEngine._instance;
52
- }
63
+ this.localUID = newUID;
53
64
 
54
- setLocalUID(localUID: string) {
55
- this.localUID = localUID;
65
+ if (!this._engine) {
66
+ this.createClientInstance();
67
+ }
56
68
  }
57
69
 
58
70
  setChannelId(channelID: string) {
59
- this.channelId = channelID;
60
- }
61
-
62
- setLoginInfo(localUID: string, channelID: string) {
63
- this.localUID = localUID;
71
+ if (
72
+ !channelID ||
73
+ typeof channelID !== 'string' ||
74
+ channelID.trim() === ''
75
+ ) {
76
+ throw new Error('setChannelId: channelID must be a non-empty string');
77
+ }
64
78
  this.channelId = channelID;
65
79
  }
66
80
 
@@ -72,16 +86,99 @@ class RTMEngine {
72
86
  return this.channelId;
73
87
  }
74
88
 
89
+ get isEngineReady() {
90
+ return !!this._engine && !!this.localUID;
91
+ }
92
+
93
+ get engine(): RTMClient {
94
+ this.ensureEngineReady();
95
+ return this._engine!;
96
+ }
97
+
98
+ private ensureEngineReady() {
99
+ if (!this.isEngineReady) {
100
+ throw new Error(
101
+ 'RTM Engine not ready. Please call setLocalUID() with a valid UID first.',
102
+ );
103
+ }
104
+ }
105
+
106
+ private createClientInstance() {
107
+ try {
108
+ if (!this.localUID || this.localUID.trim() === '') {
109
+ throw new Error('Cannot create RTM client: localUID is not set');
110
+ }
111
+ if (!$config.APP_ID) {
112
+ throw new Error('Cannot create RTM client: APP_ID is not configured');
113
+ }
114
+ const rtmConfig = new RtmConfig({
115
+ appId: $config.APP_ID,
116
+ userId: this.localUID,
117
+ });
118
+ this._engine = createAgoraRtmClient(rtmConfig);
119
+ } catch (error) {
120
+ const contextError = new Error(
121
+ `Failed to create RTM client instance for userId: ${
122
+ this.localUID
123
+ }, appId: ${$config.APP_ID}. Error: ${error.message || error}`,
124
+ );
125
+ console.error('RTMEngine createClientInstance error:', contextError);
126
+ throw contextError;
127
+ }
128
+ }
129
+
130
+ private async destroyClientInstance() {
131
+ try {
132
+ if (this._engine) {
133
+ // 1. Unsubscribe from channel if we have one
134
+ if (this.channelId) {
135
+ try {
136
+ await this._engine.unsubscribe(this.channelId);
137
+ } catch (error) {
138
+ console.warn(
139
+ `Failed to unsubscribe from channel '${this.channelId}':`,
140
+ error,
141
+ );
142
+ // Continue with cleanup even if unsubscribe fails
143
+ }
144
+ }
145
+ // 2. Remove all listeners
146
+ try {
147
+ this._engine.removeAllListeners?.();
148
+ } catch (error) {
149
+ console.warn('Failed to remove listeners:', error);
150
+ }
151
+ // 3. Logout
152
+ try {
153
+ await this._engine.logout();
154
+ if (isAndroid() || isIOS()) {
155
+ this._engine.release();
156
+ }
157
+ } catch (error) {
158
+ console.warn('Failed to logout:', error);
159
+ }
160
+ }
161
+ } catch (error) {
162
+ console.error('Error during client instance destruction:', error);
163
+ // Don't re-throw - we want cleanup to complete
164
+ }
165
+ }
166
+
75
167
  async destroy() {
76
168
  try {
77
- await this.destroyClientInstance();
78
- if (isIOS() || isAndroid()) {
79
- RTMEngine._instance = null;
169
+ if (!this._engine) {
170
+ return;
80
171
  }
81
- this.localUID = '';
172
+
173
+ await this.destroyClientInstance();
82
174
  this.channelId = '';
175
+ this.localUID = '';
176
+ this._engine = undefined;
177
+ RTMEngine._instance = null;
83
178
  } catch (error) {
84
- console.log('Error destroying instance error: ', error);
179
+ console.error('Error destroying RTM instance:', error);
180
+ // Don't re-throw - destruction should be a best-effort cleanup
181
+ // Re-throwing could prevent proper cleanup in calling code
85
182
  }
86
183
  }
87
184
  }
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  ('use strict');
14
- import RtmEngine from 'agora-react-native-rtm';
14
+ import {type RTMClient} from 'agora-react-native-rtm';
15
15
  import RTMEngine from '../rtm/RTMEngine';
16
16
  import {EventUtils} from '../rtm-events';
17
17
  import {
@@ -23,6 +23,7 @@ import {
23
23
  } from './types';
24
24
  import {adjustUID} from '../rtm/utils';
25
25
  import {LogSource, logger} from '../logger/AppBuilderLogger';
26
+ import {nativeChannelTypeMapping} from '../../bridge/rtm/web/Types';
26
27
 
27
28
  class Events {
28
29
  private source: EventSource = EventSource.core;
@@ -41,11 +42,17 @@ class Events {
41
42
  * @api private
42
43
  */
43
44
  private _persist = async (evt: string, payload: string) => {
44
- const rtmEngine: RtmEngine = RTMEngine.getInstance().engine;
45
+ const rtmEngine: RTMClient = RTMEngine.getInstance().engine;
46
+ const userId = RTMEngine.getInstance().localUid;
45
47
  try {
46
48
  const rtmAttribute = {key: evt, value: payload};
47
49
  // Step 1: Call RTM API to update local attributes
48
- await rtmEngine.addOrUpdateLocalUserAttributes([rtmAttribute]);
50
+ await rtmEngine.storage.setUserMetadata(
51
+ {items: [rtmAttribute]},
52
+ {
53
+ userId,
54
+ },
55
+ );
49
56
  } catch (error) {
50
57
  logger.error(
51
58
  LogSource.Events,
@@ -68,8 +75,8 @@ class Events {
68
75
  `CUSTOM_EVENT_API Event name cannot be of type ${typeof evt}`,
69
76
  );
70
77
  }
71
- if (evt.trim() == '') {
72
- throw Error(`CUSTOM_EVENT_API Name or function cannot be empty`);
78
+ if (evt.trim() === '') {
79
+ throw Error('CUSTOM_EVENT_API Name or function cannot be empty');
73
80
  }
74
81
  return true;
75
82
  };
@@ -103,10 +110,15 @@ class Events {
103
110
  rtmPayload: RTMAttributePayload,
104
111
  toUid?: ReceiverUid,
105
112
  ) => {
106
- const to = typeof toUid == 'string' ? parseInt(toUid) : toUid;
107
- const rtmEngine: RtmEngine = RTMEngine.getInstance().engine;
113
+ const to = typeof toUid === 'string' ? parseInt(toUid, 10) : toUid;
108
114
 
109
115
  const text = JSON.stringify(rtmPayload);
116
+
117
+ if (!RTMEngine.getInstance().isEngineReady) {
118
+ throw new Error('RTM Engine is not ready. Call setLocalUID() first.');
119
+ }
120
+ const rtmEngine: RTMClient = RTMEngine.getInstance().engine;
121
+
110
122
  // Case 1: send to channel
111
123
  if (
112
124
  typeof to === 'undefined' ||
@@ -120,7 +132,16 @@ class Events {
120
132
  );
121
133
  try {
122
134
  const channelId = RTMEngine.getInstance().channelUid;
123
- await rtmEngine.sendMessageByChannelId(channelId, text);
135
+ if (!channelId || channelId.trim() === '') {
136
+ throw new Error(
137
+ 'Channel ID is not set. Cannot send channel attributes.',
138
+ );
139
+ }
140
+ await rtmEngine.publish(channelId, text, {
141
+ channelType: nativeChannelTypeMapping.MESSAGE, // 1 is message
142
+ customType: 'PlainText',
143
+ messageType: 1,
144
+ });
124
145
  } catch (error) {
125
146
  logger.error(
126
147
  LogSource.Events,
@@ -140,10 +161,10 @@ class Events {
140
161
  );
141
162
  const adjustedUID = adjustUID(to);
142
163
  try {
143
- await rtmEngine.sendMessageToPeer({
144
- peerId: `${adjustedUID}`,
145
- offline: false,
146
- text,
164
+ await rtmEngine.publish(`${adjustedUID}`, text, {
165
+ channelType: nativeChannelTypeMapping.USER, // user
166
+ customType: 'PlainText',
167
+ messageType: 1,
147
168
  });
148
169
  } catch (error) {
149
170
  logger.error(
@@ -164,14 +185,32 @@ class Events {
164
185
  to,
165
186
  );
166
187
  try {
167
- for (const uid of to) {
168
- const adjustedUID = adjustUID(uid);
169
- await rtmEngine.sendMessageToPeer({
170
- peerId: `${adjustedUID}`,
171
- offline: false,
172
- text,
173
- });
174
- }
188
+ const response = await Promise.allSettled(
189
+ to.map(uid =>
190
+ rtmEngine.publish(`${adjustUID(uid)}`, text, {
191
+ channelType: nativeChannelTypeMapping.USER,
192
+ customType: 'PlainText',
193
+ messageType: 1,
194
+ }),
195
+ ),
196
+ );
197
+ response.forEach((result, index) => {
198
+ const uid = to[index];
199
+ if (result.status === 'rejected') {
200
+ logger.error(
201
+ LogSource.Events,
202
+ 'CUSTOM_EVENTS',
203
+ `Failed to publish to user ${uid}:`,
204
+ result.reason,
205
+ );
206
+ }
207
+ });
208
+ // for (const uid of to) {
209
+ // const adjustedUID = adjustUID(uid);
210
+ // await rtmEngine.publish(`${adjustedUID}`, text, {
211
+ // channelType: 3, // user
212
+ // });
213
+ // }
175
214
  } catch (error) {
176
215
  logger.error(
177
216
  LogSource.Events,
@@ -192,13 +231,31 @@ class Events {
192
231
  'updating channel attributes',
193
232
  );
194
233
  try {
195
- const rtmEngine: RtmEngine = RTMEngine.getInstance().engine;
234
+ // Validate if rtmengine is ready
235
+ if (!RTMEngine.getInstance().isEngineReady) {
236
+ throw new Error('RTM Engine is not ready. Call setLocalUID() first.');
237
+ }
238
+ const rtmEngine: RTMClient = RTMEngine.getInstance().engine;
239
+
196
240
  const channelId = RTMEngine.getInstance().channelUid;
241
+ if (!channelId || channelId.trim() === '') {
242
+ throw new Error(
243
+ 'Channel ID is not set. Cannot send channel attributes.',
244
+ );
245
+ }
246
+
197
247
  const rtmAttribute = [{key: rtmPayload.evt, value: rtmPayload.value}];
198
- // Step 1: Call RTM API to update local attributes
199
- await rtmEngine.addOrUpdateChannelAttributes(channelId, rtmAttribute, {
200
- enableNotificationToChannelMembers: true,
201
- });
248
+ await rtmEngine.storage.setChannelMetadata(
249
+ channelId,
250
+ nativeChannelTypeMapping.MESSAGE,
251
+ {
252
+ items: rtmAttribute,
253
+ },
254
+ {
255
+ addUserId: true,
256
+ addTimeStamp: true,
257
+ },
258
+ );
202
259
  } catch (error) {
203
260
  logger.error(
204
261
  LogSource.Events,
@@ -223,7 +280,8 @@ class Events {
223
280
  on = (eventName: string, listener: EventCallback): Function => {
224
281
  try {
225
282
  if (!this._validateEvt(eventName) || !this._validateListener(listener)) {
226
- return;
283
+ // Return no-op function instead of undefined to prevent errors
284
+ return () => {};
227
285
  }
228
286
  EventUtils.addListener(eventName, listener, this.source);
229
287
  console.log('CUSTOM_EVENT_API event listener registered', eventName);
@@ -238,6 +296,8 @@ class Events {
238
296
  'Error: events.on',
239
297
  error,
240
298
  );
299
+ // Return no-op function on error to prevent undefined issues
300
+ return () => {};
241
301
  }
242
302
  };
243
303
 
@@ -253,7 +313,11 @@ class Events {
253
313
  off = (eventName?: string, listener?: EventCallback) => {
254
314
  try {
255
315
  if (listener) {
256
- if (this._validateListener(listener) && this._validateEvt(eventName)) {
316
+ if (
317
+ eventName &&
318
+ this._validateListener(listener) &&
319
+ this._validateEvt(eventName)
320
+ ) {
257
321
  // listen off an event by eventName and listener
258
322
  //@ts-ignore
259
323
  EventUtils.removeListener(eventName, listener, this.source);
@@ -295,8 +359,18 @@ class Events {
295
359
  persistLevel: PersistanceLevel = PersistanceLevel.None,
296
360
  receiver: ReceiverUid = -1,
297
361
  ) => {
298
- if (!this._validateEvt(eventName)) {
299
- return;
362
+ try {
363
+ if (!this._validateEvt(eventName)) {
364
+ return;
365
+ }
366
+ } catch (error) {
367
+ logger.error(
368
+ LogSource.Events,
369
+ 'CUSTOM_EVENTS',
370
+ 'Event validation failed',
371
+ error,
372
+ );
373
+ return; // Don't throw - just log and return
300
374
  }
301
375
 
302
376
  const persistValue = JSON.stringify({
@@ -318,6 +392,7 @@ class Events {
318
392
  await this._persist(eventName, persistValue);
319
393
  } catch (error) {
320
394
  logger.error(LogSource.Events, 'CUSTOM_EVENTS', 'persist error', error);
395
+ // don't throw - just log the error, application should continue running
321
396
  }
322
397
  }
323
398
  try {
@@ -336,9 +411,10 @@ class Events {
336
411
  logger.error(
337
412
  LogSource.Events,
338
413
  'CUSTOM_EVENTS',
339
- 'sending event failed',
414
+ `Failed to send event '${eventName}' - event lost`,
340
415
  error,
341
416
  );
417
+ // don't throw - just log the error, application should continue running
342
418
  }
343
419
  };
344
420
  }
@@ -97,6 +97,10 @@ export const ChatTextInput = (props: ChatTextInputProps) => {
97
97
  replyToMsgId,
98
98
  setReplyToMsgId,
99
99
  } = useChatUIControls();
100
+
101
+ // Track IME composition state
102
+ const [isComposing, setIsComposing] = React.useState(false);
103
+
100
104
  const {defaultContent} = useContent();
101
105
  const {sendChatSDKMessage, uploadAttachment} = useChatConfigure();
102
106
  const {addMessageToPrivateStore, addMessageToStore} = useChatMessages();
@@ -115,6 +119,41 @@ export const ChatTextInput = (props: ChatTextInputProps) => {
115
119
  });
116
120
  }, []);
117
121
 
122
+ // Set up direct DOM event listeners for IME composition
123
+ useEffect(() => {
124
+ if (!isWeb()) return;
125
+
126
+ const inputElement = chatInputRef?.current;
127
+ if (!inputElement) return;
128
+
129
+ // Get the actual DOM element (React Native Web creates a textarea/input)
130
+ const domElement = inputElement._nativeTag
131
+ ? document.querySelector(`[data-tag="${inputElement._nativeTag}"]`)
132
+ : inputElement;
133
+
134
+ if (!domElement) return;
135
+
136
+ const handleCompositionStart = () => {
137
+ setIsComposing(true);
138
+ };
139
+
140
+ const handleCompositionEnd = () => {
141
+ setIsComposing(false);
142
+ };
143
+
144
+ // Add event listeners directly to DOM element
145
+ domElement.addEventListener('compositionstart', handleCompositionStart);
146
+ domElement.addEventListener('compositionend', handleCompositionEnd);
147
+
148
+ return () => {
149
+ domElement.removeEventListener(
150
+ 'compositionstart',
151
+ handleCompositionStart,
152
+ );
153
+ domElement.removeEventListener('compositionend', handleCompositionEnd);
154
+ };
155
+ }, [chatInputRef?.current]);
156
+
118
157
  const {data} = useRoomInfo();
119
158
  const [name] = useUserName();
120
159
  const toastHeadingSize = useString(chatSendErrorTextSizeToastHeading)();
@@ -166,9 +205,37 @@ export const ChatTextInput = (props: ChatTextInputProps) => {
166
205
  });
167
206
  };
168
207
 
208
+ // IME composition handlers
209
+ const handleCompositionStart = () => {
210
+ setIsComposing(true);
211
+ };
212
+
213
+ const handleCompositionEnd = () => {
214
+ setIsComposing(false);
215
+ };
216
+
217
+ const handleInput = event => {
218
+ // Reset composition state if input event occurs without active composition
219
+ if (isWeb() && !event.nativeEvent.isComposing && isComposing) {
220
+ setIsComposing(false);
221
+ }
222
+ };
223
+
169
224
  // with multiline textinput enter prints /n
170
225
  const handleKeyPress = ({nativeEvent}) => {
171
- if (nativeEvent.key === 'Enter' && !nativeEvent.shiftKey) {
226
+ const currentlyComposing = nativeEvent.isComposing || isComposing;
227
+
228
+ // Check if this is an Enter key during composition
229
+ if (nativeEvent.key === 'Enter' && currentlyComposing) {
230
+ return;
231
+ }
232
+
233
+ // Only submit on Enter if not composing with IME and no Shift key
234
+ if (
235
+ nativeEvent.key === 'Enter' &&
236
+ !nativeEvent.shiftKey &&
237
+ !currentlyComposing
238
+ ) {
172
239
  nativeEvent.preventDefault();
173
240
  onSubmitEditing();
174
241
  setShowEmojiPicker(false); // This will close emoji picker on enter
@@ -225,6 +292,10 @@ export const ChatTextInput = (props: ChatTextInputProps) => {
225
292
  autoCorrect={false}
226
293
  onKeyPress={handleKeyPress}
227
294
  onChange={_handleHeightChange}
295
+ // IME composition event handlers for React Native Web
296
+ onCompositionStart={isWeb() ? handleCompositionStart : undefined}
297
+ onCompositionEnd={isWeb() ? handleCompositionEnd : undefined}
298
+ onInput={isWeb() ? handleInput : undefined}
228
299
  />
229
300
  );
230
301
 
@@ -121,7 +121,7 @@ const useSTTAPI = (): IuseSTTAPI => {
121
121
  const start = async (lang: LanguageType[]) => {
122
122
  try {
123
123
  setIsLangChangeInProgress(true);
124
- const res = await apiCall('start', lang);
124
+ const res = await apiCall('startv7', lang);
125
125
  // null means stt startred successfully
126
126
  const isSTTAlreadyActive =
127
127
  res?.error?.message
@@ -209,7 +209,7 @@ const useSTTAPI = (): IuseSTTAPI => {
209
209
 
210
210
  const stop = async () => {
211
211
  try {
212
- const res = await apiCall('stop');
212
+ const res = await apiCall('stopv7');
213
213
  // once STT is non-active in the channel , notify others so that they dont' trigger start again
214
214
  // events.send(
215
215
  // EventNames.STT_ACTIVE,
@@ -12,20 +12,38 @@ export function formatTime(timestamp: number): string {
12
12
  }
13
13
 
14
14
  export type LanguageType =
15
- | 'en-US'
16
- | 'hi-IN'
15
+ | 'ar-EG'
16
+ | 'ar-JO'
17
+ | 'ar-SA'
18
+ | 'ar-AE'
19
+ | 'bn-IN'
17
20
  | 'zh-CN'
18
21
  | 'zh-HK'
22
+ | 'zh-TW'
23
+ | 'nl-NL'
24
+ | 'en-IN'
25
+ | 'en-US'
26
+ | 'fil-PH'
19
27
  | 'fr-FR'
20
28
  | 'de-DE'
21
- | 'ko-KR'
22
- | 'en-IN'
23
- | 'ar'
29
+ | 'gu-IN'
30
+ | 'he-IL'
31
+ | 'hi-IN'
32
+ | 'id-ID'
33
+ | 'it-IT'
24
34
  | 'ja-JP'
35
+ | 'kn-IN'
36
+ | 'ko-KR'
37
+ | 'ms-MY'
38
+ | 'fa-IR'
25
39
  | 'pt-PT'
40
+ | 'ru-RU'
26
41
  | 'es-ES'
27
- | 'it-IT'
28
- | 'id-ID'
42
+ | 'ta-IN'
43
+ | 'te-IN'
44
+ | 'th-TH'
45
+ | 'tr-TR'
46
+ | 'vi-VN'
29
47
  | '';
30
48
 
31
49
  interface LanguageData {
@@ -34,20 +52,38 @@ interface LanguageData {
34
52
  }
35
53
 
36
54
  export const langData: LanguageData[] = [
55
+ {label: 'Arabic (EG)', value: 'ar-EG'},
56
+ {label: 'Arabic (JO)', value: 'ar-JO'},
57
+ {label: 'Arabic (SA)', value: 'ar-SA'},
58
+ {label: 'Arabic (UAE)', value: 'ar-AE'},
59
+ {label: 'Bengali (IN)', value: 'bn-IN'},
60
+ {label: 'Chinese', value: 'zh-CN'},
61
+ {label: 'Chinese (HK)', value: 'zh-HK'},
62
+ {label: 'Chinese (TW)', value: 'zh-TW'},
63
+ {label: 'Dutch', value: 'nl-NL'},
64
+ {label: 'English (IN)', value: 'en-IN'},
37
65
  {label: 'English (US)', value: 'en-US'},
38
- {label: 'English (India)', value: 'en-IN'},
39
- {label: 'Hindi', value: 'hi-IN'},
40
- {label: 'Chinese (Simplified)', value: 'zh-CN'},
41
- {label: 'Chinese (Traditional)', value: 'zh-HK'},
42
- {label: 'Arabic', value: 'ar'},
66
+ {label: 'Filipino', value: 'fil-PH'},
43
67
  {label: 'French', value: 'fr-FR'},
44
68
  {label: 'German', value: 'de-DE'},
69
+ {label: 'Gujarati', value: 'gu-IN'},
70
+ {label: 'Hebrew', value: 'he-IL'},
71
+ {label: 'Hindi', value: 'hi-IN'},
72
+ {label: 'Indonesian', value: 'id-ID'},
73
+ {label: 'Italian', value: 'it-IT'},
45
74
  {label: 'Japanese', value: 'ja-JP'},
75
+ {label: 'Kannada', value: 'kn-IN'},
46
76
  {label: 'Korean', value: 'ko-KR'},
77
+ {label: 'Malay', value: 'ms-MY'},
78
+ {label: 'Persian', value: 'fa-IR'},
47
79
  {label: 'Portuguese', value: 'pt-PT'},
80
+ {label: 'Russian', value: 'ru-RU'},
48
81
  {label: 'Spanish', value: 'es-ES'},
49
- {label: 'Italian', value: 'it-IT'},
50
- {label: 'Indonesian', value: 'id-ID'},
82
+ {label: 'Tamil', value: 'ta-IN'},
83
+ {label: 'Telugu', value: 'te-IN'},
84
+ {label: 'Thai', value: 'th-TH'},
85
+ {label: 'Turkish', value: 'tr-TR'},
86
+ {label: 'Vietnamese', value: 'vi-VN'},
51
87
  ];
52
88
 
53
89
  export function getLanguageLabel(
@@ -69,7 +69,7 @@ const useEndCall = () => {
69
69
  if ($config.CHAT) {
70
70
  deleteChatUser();
71
71
  }
72
- RTMEngine.getInstance().engine.leaveChannel(rtcProps.channel);
72
+ RTMEngine.getInstance().engine.unsubscribe(rtcProps.channel);
73
73
  if (!ENABLE_AUTH) {
74
74
  // await authLogout();
75
75
  await authLogin();
@@ -58,7 +58,6 @@ export default function useJoinRoom() {
58
58
  send_event: false,
59
59
  });
60
60
  } else {
61
- console.log('debugging store.token', store.token);
62
61
  logger.log(
63
62
  LogSource.NetworkRest,
64
63
  'joinChannel',