agora-appbuilder-core 4.1.9 → 4.1.11-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 (58) hide show
  1. package/package.json +2 -2
  2. package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -3
  3. package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -2
  4. package/template/agora-rn-uikit/src/Reducer/index.ts +0 -2
  5. package/template/agora-rn-uikit/src/Rtc/Join.tsx +11 -25
  6. package/template/agora-rn-uikit/src/RtcConfigure.tsx +1 -14
  7. package/template/agora-rn-uikit/src/Utils/isBotUser.ts +1 -1
  8. package/template/android/app/build.gradle +0 -7
  9. package/template/bridge/rtm/web/Types.ts +0 -183
  10. package/template/bridge/rtm/web/index.ts +491 -423
  11. package/template/defaultConfig.js +3 -3
  12. package/template/ios/Podfile +0 -41
  13. package/template/package.json +5 -5
  14. package/template/src/assets/font-styles.css +4 -0
  15. package/template/src/assets/fonts/icomoon.ttf +0 -0
  16. package/template/src/assets/selection.json +1 -1
  17. package/template/src/atoms/ActionMenu.tsx +93 -13
  18. package/template/src/atoms/CustomIcon.tsx +1 -0
  19. package/template/src/atoms/DropDownMulti.tsx +80 -29
  20. package/template/src/atoms/Input.tsx +2 -1
  21. package/template/src/components/Controls.tsx +148 -143
  22. package/template/src/components/EventsConfigure.tsx +152 -97
  23. package/template/src/components/RTMConfigure.tsx +426 -644
  24. package/template/src/components/precall/joinCallBtn.native.tsx +7 -2
  25. package/template/src/components/precall/joinCallBtn.tsx +7 -2
  26. package/template/src/components/precall/joinWaitingRoomBtn.native.tsx +8 -3
  27. package/template/src/components/precall/joinWaitingRoomBtn.tsx +22 -4
  28. package/template/src/components/precall/textInput.tsx +45 -22
  29. package/template/src/components/precall/usePreCall.tsx +7 -0
  30. package/template/src/components/room-info/useRoomInfo.tsx +5 -0
  31. package/template/src/language/default-labels/videoCallScreenLabels.ts +27 -4
  32. package/template/src/pages/video-call/ActionSheetContent.tsx +77 -77
  33. package/template/src/pages/video-call/SidePanelHeader.tsx +81 -36
  34. package/template/src/rtm/RTMEngine.ts +33 -130
  35. package/template/src/rtm-events/constants.ts +6 -0
  36. package/template/src/rtm-events-api/Events.ts +30 -106
  37. package/template/src/subComponents/caption/Caption.tsx +48 -7
  38. package/template/src/subComponents/caption/CaptionContainer.tsx +324 -51
  39. package/template/src/subComponents/caption/CaptionIcon.tsx +35 -34
  40. package/template/src/subComponents/caption/CaptionText.tsx +103 -2
  41. package/template/src/subComponents/caption/LanguageSelectorPopup.tsx +179 -69
  42. package/template/src/subComponents/caption/Transcript.tsx +46 -11
  43. package/template/src/subComponents/caption/TranscriptIcon.tsx +27 -35
  44. package/template/src/subComponents/caption/TranscriptText.tsx +78 -3
  45. package/template/src/subComponents/caption/proto/ptoto.js +38 -4
  46. package/template/src/subComponents/caption/proto/test.proto +34 -19
  47. package/template/src/subComponents/caption/useCaption.tsx +753 -10
  48. package/template/src/subComponents/caption/useSTTAPI.tsx +118 -205
  49. package/template/src/subComponents/caption/useStreamMessageUtils.native.ts +152 -33
  50. package/template/src/subComponents/caption/useStreamMessageUtils.ts +165 -34
  51. package/template/src/subComponents/caption/utils.ts +171 -3
  52. package/template/src/utils/SdkEvents.ts +3 -0
  53. package/template/src/utils/useEndCall.ts +3 -5
  54. package/template/src/utils/useSpeechToText.ts +31 -20
  55. package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +0 -11
  56. package/template/agora-rn-uikit/src/Reducer/UserBanned.ts +0 -11
  57. package/template/bridge/rtm/web/index-legacy.ts +0 -540
  58. package/template/src/components/RTMConfigure-legacy.tsx +0 -848
@@ -20,13 +20,20 @@ import {
20
20
  } from '../../components/chat-ui/useChatUIControls';
21
21
  import {numFormatter} from '../../utils';
22
22
  import ChatContext from '../../components/ChatContext';
23
- import {useCaption} from '../../subComponents/caption/useCaption';
23
+ import {
24
+ LanguageTranslationConfig,
25
+ useCaption,
26
+ } from '../../subComponents/caption/useCaption';
24
27
  import ActionMenu, {ActionMenuItem} from '../../atoms/ActionMenu';
25
28
  import {calculatePosition, isMobileUA} from '../../utils/common';
26
29
  import LanguageSelectorPopup from '../../subComponents/caption/LanguageSelectorPopup';
27
30
  import useSTTAPI from '../../subComponents/caption/useSTTAPI';
28
31
  import useGetName from '../../utils/useGetName';
29
- import {LanguageType} from '../../subComponents/caption/utils';
32
+ import {
33
+ LanguageType,
34
+ mergeTranslationConfigs,
35
+ TranslateConfig,
36
+ } from '../../subComponents/caption/utils';
30
37
  import {useRoomInfo, usePreCall} from 'customization-api';
31
38
  import useTranscriptDownload from '../../subComponents/caption/useTranscriptDownload';
32
39
  import {useVB} from '../../components/virtual-background/useVB';
@@ -42,10 +49,13 @@ import {
42
49
  chatPanelPrivateTabText,
43
50
  peoplePanelHeaderText,
44
51
  sttChangeSpokenLanguageText,
52
+ sttChangeTranslationLanguageText,
45
53
  sttDownloadTranscriptBtnText,
54
+ sttStopTranslationText,
46
55
  sttTranscriptPanelHeaderText,
47
56
  } from '../../language/default-labels/videoCallScreenLabels';
48
57
  import {logger, LogSource} from '../../logger/AppBuilderLogger';
58
+ import {TranslateActionMenu} from '../../subComponents/caption/CaptionContainer';
49
59
 
50
60
  export const SettingsHeader = props => {
51
61
  const {setSidePanel} = useSidePanel();
@@ -262,7 +272,7 @@ const TranscriptHeaderActionMenu = (props: TranscriptHeaderActionMenuProps) => {
262
272
  language: prevLang,
263
273
  meetingTranscript,
264
274
  isLangChangeInProgress,
265
- setLanguage,
275
+ selectedTranslationLanguage,
266
276
  } = useCaption();
267
277
  const {downloadTranscript} = useTranscriptDownload();
268
278
  const [modalPosition, setModalPosition] = React.useState({});
@@ -270,29 +280,67 @@ const TranscriptHeaderActionMenu = (props: TranscriptHeaderActionMenuProps) => {
270
280
  const {width: globalWidth, height: globalHeight} = useWindowDimensions();
271
281
  const [isLanguagePopupOpen, setLanguagePopup] =
272
282
  React.useState<boolean>(false);
283
+ const [isTranslateMenuOpen, setTranslateMenuOpen] =
284
+ React.useState<boolean>(false);
273
285
  const {restart} = useSTTAPI();
274
286
  const username = useGetName();
275
287
  const actionMenuitems: ActionMenuItem[] = [];
276
288
  const {
277
289
  data: {isHost},
290
+ sttLanguage,
278
291
  } = useRoomInfo();
279
-
292
+ const {handleTranslateConfigChange, updateSTTBotSession, translationConfig} =
293
+ useCaption();
280
294
  const downloadTranscriptLabel = useString(sttDownloadTranscriptBtnText)();
281
295
  const changeSpokenLanguage = useString<boolean>(
282
296
  sttChangeSpokenLanguageText,
283
297
  )();
284
- isHost &&
285
- actionMenuitems.push({
286
- icon: 'lang-select',
287
- iconColor: $config.SECONDARY_ACTION_COLOR,
288
- textColor: $config.FONT_COLOR,
289
- title: changeSpokenLanguage + ' ',
290
- disabled: isLangChangeInProgress,
291
- onPress: () => {
292
- setActionMenuVisible(false);
293
- setLanguagePopup(true);
294
- },
295
- });
298
+ const sttStopTranslationLabel = useString<boolean>(sttStopTranslationText)();
299
+ const changeTranslationLanguage = useString<boolean>(
300
+ sttChangeTranslationLanguageText,
301
+ )();
302
+
303
+ // isHost &&
304
+ actionMenuitems.push({
305
+ icon: 'globe',
306
+ iconColor: $config.SECONDARY_ACTION_COLOR,
307
+ textColor: $config.FONT_COLOR,
308
+ title: changeSpokenLanguage + ' ',
309
+ disabled: isLangChangeInProgress,
310
+ onPress: () => {
311
+ setActionMenuVisible(false);
312
+ setLanguagePopup(true);
313
+ },
314
+ });
315
+
316
+ // actionMenuitems.push({
317
+ // icon: 'lang-select',
318
+ // iconColor: $config.SECONDARY_ACTION_COLOR,
319
+ // textColor: $config.FONT_COLOR,
320
+ // title: changeTranslationLanguage,
321
+ // disabled: false,
322
+ // onPress: () => {
323
+ // setActionMenuVisible(false);
324
+ // setTranslateMenuOpen(true);
325
+ // },
326
+ // });
327
+
328
+ actionMenuitems.push({
329
+ icon: 'lang-select',
330
+ iconColor: $config.SECONDARY_ACTION_COLOR,
331
+ textColor: $config.FONT_COLOR,
332
+ title: sttStopTranslationLabel,
333
+ disabled: false,
334
+ onPress: async () => {
335
+ setActionMenuVisible(false);
336
+ // Keep source language same, just clear target languages
337
+ // This stops translation but keeps transcription running
338
+ await updateSTTBotSession({
339
+ source: translationConfig.source, // Keep current source
340
+ targets: [], // Empty targets = no translation
341
+ });
342
+ },
343
+ });
296
344
 
297
345
  actionMenuitems.push({
298
346
  icon: 'download',
@@ -306,27 +354,18 @@ const TranscriptHeaderActionMenu = (props: TranscriptHeaderActionMenuProps) => {
306
354
  },
307
355
  });
308
356
 
309
- const onLanguageChange = (langChanged = false, language: LanguageType[]) => {
357
+ const onLanguageChange = async (
358
+ inputTranslateConfig: LanguageTranslationConfig,
359
+ ) => {
360
+ console.log(
361
+ '[STT_PER_USER_BOT] SidePanelHeader TranscriptHeaderActionMenu - onLanguageChange - sourceLanguage , targetLanguage:',
362
+ inputTranslateConfig,
363
+ );
310
364
  setLanguagePopup(false);
311
- if (langChanged) {
312
- restart(language)
313
- .then(() => {
314
- logger.debug(
315
- LogSource.Internals,
316
- 'STT',
317
- 'stt restarted successfully',
318
- );
319
- })
320
- .catch(error => {
321
- logger.error(
322
- LogSource.Internals,
323
- 'STT',
324
- 'Error in restarting',
325
- error,
326
- );
327
- // Handle the error case
328
- });
329
- }
365
+ // Update caption with new language configuration\
366
+ try {
367
+ await handleTranslateConfigChange(inputTranslateConfig);
368
+ } catch (error) {}
330
369
  };
331
370
 
332
371
  React.useEffect(() => {
@@ -370,6 +409,12 @@ const TranscriptHeaderActionMenu = (props: TranscriptHeaderActionMenuProps) => {
370
409
  setModalVisible={setLanguagePopup}
371
410
  onConfirm={onLanguageChange}
372
411
  />
412
+
413
+ <TranslateActionMenu
414
+ actionMenuVisible={isTranslateMenuOpen}
415
+ setActionMenuVisible={setTranslateMenuOpen}
416
+ btnRef={btnRef}
417
+ />
373
418
  </>
374
419
  );
375
420
  };
@@ -10,71 +10,57 @@
10
10
  *********************************************
11
11
  */
12
12
 
13
- import {
14
- createAgoraRtmClient,
15
- RtmConfig,
16
- type RTMClient,
17
- } from 'agora-react-native-rtm';
13
+ import RtmEngine from 'agora-react-native-rtm';
18
14
  import {isAndroid, isIOS} from '../utils/common';
19
15
 
20
16
  class RTMEngine {
21
- private _engine?: RTMClient;
17
+ engine!: RtmEngine;
22
18
  private localUID: string = '';
23
19
  private channelId: string = '';
24
20
 
25
21
  private static _instance: RTMEngine | null = null;
26
22
 
27
- private constructor() {
28
- if (RTMEngine._instance) {
29
- return RTMEngine._instance;
30
- }
31
- RTMEngine._instance = this;
32
- return RTMEngine._instance;
33
- }
34
-
35
23
  public static getInstance() {
36
- // We are only creating the instance but not creating the rtm client yet
37
24
  if (!RTMEngine._instance) {
38
- RTMEngine._instance = new RTMEngine();
25
+ return new RTMEngine();
39
26
  }
40
27
  return RTMEngine._instance;
41
28
  }
42
29
 
43
- setLocalUID(localUID: string | number) {
44
- if (localUID === null || localUID === undefined) {
45
- throw new Error('setLocalUID: localUID cannot be null or undefined');
46
- }
30
+ private async createClientInstance() {
31
+ await this.engine.createClient($config.APP_ID);
32
+ }
47
33
 
48
- const newUID = String(localUID);
49
- if (newUID.trim() === '') {
50
- throw new Error(
51
- 'setLocalUID: localUID cannot be empty after string conversion',
52
- );
34
+ private async destroyClientInstance() {
35
+ await this.engine.logout();
36
+ if (isIOS() || isAndroid()) {
37
+ await this.engine.destroyClient();
53
38
  }
39
+ }
54
40
 
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
- );
41
+ private constructor() {
42
+ if (RTMEngine._instance) {
43
+ return RTMEngine._instance;
61
44
  }
45
+ RTMEngine._instance = this;
46
+ this.engine = new RtmEngine();
47
+ this.localUID = '';
48
+ this.channelId = '';
49
+ this.createClientInstance();
62
50
 
63
- this.localUID = newUID;
51
+ return RTMEngine._instance;
52
+ }
64
53
 
65
- if (!this._engine) {
66
- this.createClientInstance();
67
- }
54
+ setLocalUID(localUID: string) {
55
+ this.localUID = localUID;
68
56
  }
69
57
 
70
58
  setChannelId(channelID: string) {
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
- }
59
+ this.channelId = channelID;
60
+ }
61
+
62
+ setLoginInfo(localUID: string, channelID: string) {
63
+ this.localUID = localUID;
78
64
  this.channelId = channelID;
79
65
  }
80
66
 
@@ -86,99 +72,16 @@ class RTMEngine {
86
72
  return this.channelId;
87
73
  }
88
74
 
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
-
167
75
  async destroy() {
168
76
  try {
169
- if (!this._engine) {
170
- return;
171
- }
172
-
173
77
  await this.destroyClientInstance();
174
- this.channelId = '';
78
+ if (isIOS() || isAndroid()) {
79
+ RTMEngine._instance = null;
80
+ }
175
81
  this.localUID = '';
176
- this._engine = undefined;
177
- RTMEngine._instance = null;
82
+ this.channelId = '';
178
83
  } catch (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
84
+ console.log('Error destroying instance error: ', error);
182
85
  }
183
86
  }
184
87
  }
@@ -30,6 +30,9 @@ const VIDEO_MEETING_ATTENDEE = 'VIDEO_MEETING_ATTENDEE';
30
30
  // 6. STT
31
31
  const STT_ACTIVE = 'STT_IS_ACTIVE';
32
32
  const STT_LANGUAGE = 'STT_LANGUAGE_CHANGED';
33
+ const STT_TRANSLATE_LANGUAGE = 'STT_TRANSLATE_LANGUAGE_CHANGED';
34
+ const STT_SPOKEN_LANGUAGE = 'STT_SPOKEN_LANGUAGE';
35
+ const USER_STOPPED_TRANSLATION = 'USER_STOPPED_TRANSLATION';
33
36
  // 7. WAITING ROOM
34
37
  const WAITING_ROOM_REQUEST = 'WAITING_ROOM_REQUEST';
35
38
  const WAITING_ROOM_RESPONSE = 'WAITING_ROOM_RESPONSE';
@@ -53,6 +56,9 @@ const EventNames = {
53
56
  VIDEO_MEETING_ATTENDEE,
54
57
  STT_ACTIVE,
55
58
  STT_LANGUAGE,
59
+ STT_TRANSLATE_LANGUAGE,
60
+ STT_SPOKEN_LANGUAGE,
61
+ USER_STOPPED_TRANSLATION,
56
62
  WAITING_ROOM_REQUEST,
57
63
  WAITING_ROOM_RESPONSE,
58
64
  WAITING_ROOM_STATUS_UPDATE,
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  ('use strict');
14
- import {type RTMClient} from 'agora-react-native-rtm';
14
+ import RtmEngine from 'agora-react-native-rtm';
15
15
  import RTMEngine from '../rtm/RTMEngine';
16
16
  import {EventUtils} from '../rtm-events';
17
17
  import {
@@ -23,7 +23,6 @@ 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';
27
26
 
28
27
  class Events {
29
28
  private source: EventSource = EventSource.core;
@@ -42,17 +41,11 @@ class Events {
42
41
  * @api private
43
42
  */
44
43
  private _persist = async (evt: string, payload: string) => {
45
- const rtmEngine: RTMClient = RTMEngine.getInstance().engine;
46
- const userId = RTMEngine.getInstance().localUid;
44
+ const rtmEngine: RtmEngine = RTMEngine.getInstance().engine;
47
45
  try {
48
46
  const rtmAttribute = {key: evt, value: payload};
49
47
  // Step 1: Call RTM API to update local attributes
50
- await rtmEngine.storage.setUserMetadata(
51
- {items: [rtmAttribute]},
52
- {
53
- userId,
54
- },
55
- );
48
+ await rtmEngine.addOrUpdateLocalUserAttributes([rtmAttribute]);
56
49
  } catch (error) {
57
50
  logger.error(
58
51
  LogSource.Events,
@@ -75,8 +68,8 @@ class Events {
75
68
  `CUSTOM_EVENT_API Event name cannot be of type ${typeof evt}`,
76
69
  );
77
70
  }
78
- if (evt.trim() === '') {
79
- throw Error('CUSTOM_EVENT_API Name or function cannot be empty');
71
+ if (evt.trim() == '') {
72
+ throw Error(`CUSTOM_EVENT_API Name or function cannot be empty`);
80
73
  }
81
74
  return true;
82
75
  };
@@ -110,15 +103,10 @@ class Events {
110
103
  rtmPayload: RTMAttributePayload,
111
104
  toUid?: ReceiverUid,
112
105
  ) => {
113
- const to = typeof toUid === 'string' ? parseInt(toUid, 10) : toUid;
106
+ const to = typeof toUid == 'string' ? parseInt(toUid) : toUid;
107
+ const rtmEngine: RtmEngine = RTMEngine.getInstance().engine;
114
108
 
115
109
  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
-
122
110
  // Case 1: send to channel
123
111
  if (
124
112
  typeof to === 'undefined' ||
@@ -132,16 +120,7 @@ class Events {
132
120
  );
133
121
  try {
134
122
  const channelId = RTMEngine.getInstance().channelUid;
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
- });
123
+ await rtmEngine.sendMessageByChannelId(channelId, text);
145
124
  } catch (error) {
146
125
  logger.error(
147
126
  LogSource.Events,
@@ -161,10 +140,10 @@ class Events {
161
140
  );
162
141
  const adjustedUID = adjustUID(to);
163
142
  try {
164
- await rtmEngine.publish(`${adjustedUID}`, text, {
165
- channelType: nativeChannelTypeMapping.USER, // user
166
- customType: 'PlainText',
167
- messageType: 1,
143
+ await rtmEngine.sendMessageToPeer({
144
+ peerId: `${adjustedUID}`,
145
+ offline: false,
146
+ text,
168
147
  });
169
148
  } catch (error) {
170
149
  logger.error(
@@ -185,32 +164,14 @@ class Events {
185
164
  to,
186
165
  );
187
166
  try {
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
- // }
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
+ }
214
175
  } catch (error) {
215
176
  logger.error(
216
177
  LogSource.Events,
@@ -231,31 +192,13 @@ class Events {
231
192
  'updating channel attributes',
232
193
  );
233
194
  try {
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
-
195
+ const rtmEngine: RtmEngine = RTMEngine.getInstance().engine;
240
196
  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
-
247
197
  const rtmAttribute = [{key: rtmPayload.evt, value: rtmPayload.value}];
248
- await rtmEngine.storage.setChannelMetadata(
249
- channelId,
250
- nativeChannelTypeMapping.MESSAGE,
251
- {
252
- items: rtmAttribute,
253
- },
254
- {
255
- addUserId: true,
256
- addTimeStamp: true,
257
- },
258
- );
198
+ // Step 1: Call RTM API to update local attributes
199
+ await rtmEngine.addOrUpdateChannelAttributes(channelId, rtmAttribute, {
200
+ enableNotificationToChannelMembers: true,
201
+ });
259
202
  } catch (error) {
260
203
  logger.error(
261
204
  LogSource.Events,
@@ -280,8 +223,7 @@ class Events {
280
223
  on = (eventName: string, listener: EventCallback): Function => {
281
224
  try {
282
225
  if (!this._validateEvt(eventName) || !this._validateListener(listener)) {
283
- // Return no-op function instead of undefined to prevent errors
284
- return () => {};
226
+ return;
285
227
  }
286
228
  EventUtils.addListener(eventName, listener, this.source);
287
229
  console.log('CUSTOM_EVENT_API event listener registered', eventName);
@@ -296,8 +238,6 @@ class Events {
296
238
  'Error: events.on',
297
239
  error,
298
240
  );
299
- // Return no-op function on error to prevent undefined issues
300
- return () => {};
301
241
  }
302
242
  };
303
243
 
@@ -313,11 +253,7 @@ class Events {
313
253
  off = (eventName?: string, listener?: EventCallback) => {
314
254
  try {
315
255
  if (listener) {
316
- if (
317
- eventName &&
318
- this._validateListener(listener) &&
319
- this._validateEvt(eventName)
320
- ) {
256
+ if (this._validateListener(listener) && this._validateEvt(eventName)) {
321
257
  // listen off an event by eventName and listener
322
258
  //@ts-ignore
323
259
  EventUtils.removeListener(eventName, listener, this.source);
@@ -359,18 +295,8 @@ class Events {
359
295
  persistLevel: PersistanceLevel = PersistanceLevel.None,
360
296
  receiver: ReceiverUid = -1,
361
297
  ) => {
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
298
+ if (!this._validateEvt(eventName)) {
299
+ return;
374
300
  }
375
301
 
376
302
  const persistValue = JSON.stringify({
@@ -392,7 +318,6 @@ class Events {
392
318
  await this._persist(eventName, persistValue);
393
319
  } catch (error) {
394
320
  logger.error(LogSource.Events, 'CUSTOM_EVENTS', 'persist error', error);
395
- // don't throw - just log the error, application should continue running
396
321
  }
397
322
  }
398
323
  try {
@@ -411,10 +336,9 @@ class Events {
411
336
  logger.error(
412
337
  LogSource.Events,
413
338
  'CUSTOM_EVENTS',
414
- `Failed to send event '${eventName}' - event lost`,
339
+ 'sending event failed',
415
340
  error,
416
341
  );
417
- // don't throw - just log the error, application should continue running
418
342
  }
419
343
  };
420
344
  }