agora-appbuilder-core 4.1.10-beta.1 → 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 (132) hide show
  1. package/package.json +2 -2
  2. package/template/agora-rn-uikit/src/Utils/isBotUser.ts +1 -1
  3. package/template/android/app/build.gradle +0 -7
  4. package/template/bridge/rtc/webNg/RtcEngine.ts +2 -2
  5. package/template/bridge/rtm/web/Types.ts +0 -183
  6. package/template/bridge/rtm/web/index.ts +488 -450
  7. package/template/customization-api/typeDefinition.ts +0 -1
  8. package/template/defaultConfig.js +3 -4
  9. package/template/global.d.ts +0 -1
  10. package/template/ios/Podfile +0 -41
  11. package/template/package.json +5 -5
  12. package/template/src/AppRoutes.tsx +3 -3
  13. package/template/src/ai-agent/components/ControlButtons.tsx +1 -1
  14. package/template/src/assets/font-styles.css +1 -33
  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 -8
  19. package/template/src/atoms/DropDownMulti.tsx +80 -29
  20. package/template/src/atoms/Dropdown.tsx +0 -5
  21. package/template/src/atoms/Input.tsx +2 -1
  22. package/template/src/atoms/TertiaryButton.tsx +1 -1
  23. package/template/src/atoms/UserAvatar.tsx +1 -1
  24. package/template/src/components/ChatContext.ts +3 -5
  25. package/template/src/components/Controls.tsx +167 -208
  26. package/template/src/components/DeviceConfigure.tsx +1 -1
  27. package/template/src/components/EventsConfigure.tsx +168 -118
  28. package/template/src/components/Navbar.tsx +11 -14
  29. package/template/src/components/RTMConfigure.tsx +819 -32
  30. package/template/src/components/beauty-effect/useBeautyEffects.tsx +13 -50
  31. package/template/src/components/chat/chatConfigure.tsx +1 -7
  32. package/template/src/components/chat-messages/useChatMessages.tsx +11 -43
  33. package/template/src/components/controls/useControlPermissionMatrix.tsx +4 -32
  34. package/template/src/components/participants/AllHostParticipants.tsx +2 -10
  35. package/template/src/components/participants/Participant.tsx +1 -7
  36. package/template/src/components/participants/UserActionMenuOptions.tsx +2 -12
  37. package/template/src/components/precall/joinCallBtn.native.tsx +7 -2
  38. package/template/src/components/precall/joinCallBtn.tsx +7 -2
  39. package/template/src/components/precall/joinWaitingRoomBtn.native.tsx +16 -15
  40. package/template/src/components/precall/joinWaitingRoomBtn.tsx +31 -17
  41. package/template/src/components/precall/textInput.tsx +45 -22
  42. package/template/src/components/precall/usePreCall.tsx +7 -0
  43. package/template/src/components/recordings/RecordingsDateTable.tsx +2 -3
  44. package/template/src/components/room-info/useRoomInfo.tsx +5 -0
  45. package/template/src/components/useUserPreference.tsx +12 -39
  46. package/template/src/components/virtual-background/useVB.tsx +0 -18
  47. package/template/src/components/whiteboard/WhiteboardConfigure.tsx +0 -27
  48. package/template/src/language/default-labels/videoCallScreenLabels.ts +27 -11
  49. package/template/src/logger/AppBuilderLogger.tsx +3 -11
  50. package/template/src/pages/VideoCall.tsx +518 -171
  51. package/template/src/pages/video-call/ActionSheetContent.tsx +77 -77
  52. package/template/src/pages/video-call/SidePanelHeader.tsx +81 -53
  53. package/template/src/pages/video-call/VideoCallScreen.tsx +0 -18
  54. package/template/src/pages/video-call/VideoCallScreenWrapper.tsx +1 -0
  55. package/template/src/rtm/RTMEngine.ts +37 -262
  56. package/template/src/rtm/utils.ts +1 -68
  57. package/template/src/rtm-events/constants.ts +7 -40
  58. package/template/src/rtm-events-api/Events.ts +39 -158
  59. package/template/src/subComponents/ChatBubble.tsx +3 -3
  60. package/template/src/subComponents/ChatContainer.tsx +9 -19
  61. package/template/src/subComponents/LocalAudioMute.tsx +2 -2
  62. package/template/src/subComponents/LocalVideoMute.tsx +2 -2
  63. package/template/src/subComponents/SidePanelEnum.tsx +0 -1
  64. package/template/src/subComponents/caption/Caption.tsx +48 -7
  65. package/template/src/subComponents/caption/CaptionContainer.tsx +324 -51
  66. package/template/src/subComponents/caption/CaptionIcon.tsx +35 -34
  67. package/template/src/subComponents/caption/CaptionText.tsx +103 -2
  68. package/template/src/subComponents/caption/LanguageSelectorPopup.tsx +179 -69
  69. package/template/src/subComponents/caption/Transcript.tsx +46 -11
  70. package/template/src/subComponents/caption/TranscriptIcon.tsx +27 -35
  71. package/template/src/subComponents/caption/TranscriptText.tsx +78 -3
  72. package/template/src/subComponents/caption/proto/ptoto.js +38 -4
  73. package/template/src/subComponents/caption/proto/test.proto +34 -19
  74. package/template/src/subComponents/caption/useCaption.tsx +754 -11
  75. package/template/src/subComponents/caption/useSTTAPI.tsx +118 -205
  76. package/template/src/subComponents/caption/useStreamMessageUtils.native.ts +152 -33
  77. package/template/src/subComponents/caption/useStreamMessageUtils.ts +165 -34
  78. package/template/src/subComponents/caption/utils.ts +171 -3
  79. package/template/src/subComponents/chat/ChatSendButton.tsx +0 -1
  80. package/template/src/subComponents/screenshare/ScreenshareButton.tsx +0 -16
  81. package/template/src/subComponents/screenshare/ScreenshareConfigure.native.tsx +1 -1
  82. package/template/src/subComponents/waiting-rooms/WaitingRoomControls.tsx +4 -7
  83. package/template/src/utils/SdkEvents.ts +3 -0
  84. package/template/src/utils/useEndCall.ts +4 -4
  85. package/template/src/utils/useMuteToggleLocal.ts +10 -14
  86. package/template/src/utils/useSpeechToText.ts +31 -20
  87. package/template/bridge/rtm/web/index-legacy.ts +0 -540
  88. package/template/src/components/RTMConfigure-legacy.tsx +0 -848
  89. package/template/src/components/UserGlobalPreferenceProvider.tsx +0 -227
  90. package/template/src/components/breakout-room/BreakoutRoomPanel.tsx +0 -58
  91. package/template/src/components/breakout-room/context/BreakoutRoomContext.tsx +0 -2508
  92. package/template/src/components/breakout-room/events/BreakoutRoomEventsConfigure.tsx +0 -272
  93. package/template/src/components/breakout-room/events/constants.ts +0 -17
  94. package/template/src/components/breakout-room/hoc/BreakoutRoomNameRenderer.tsx +0 -68
  95. package/template/src/components/breakout-room/hooks/useBreakoutRoomExit.ts +0 -49
  96. package/template/src/components/breakout-room/state/reducer.ts +0 -522
  97. package/template/src/components/breakout-room/state/types.ts +0 -54
  98. package/template/src/components/breakout-room/ui/BreakoutMeetingTitle.tsx +0 -60
  99. package/template/src/components/breakout-room/ui/BreakoutRoomActionMenu.tsx +0 -136
  100. package/template/src/components/breakout-room/ui/BreakoutRoomAnnouncementModal.tsx +0 -135
  101. package/template/src/components/breakout-room/ui/BreakoutRoomGroupSettings.tsx +0 -588
  102. package/template/src/components/breakout-room/ui/BreakoutRoomMainRoomUsers.tsx +0 -142
  103. package/template/src/components/breakout-room/ui/BreakoutRoomMemberActionMenu.tsx +0 -122
  104. package/template/src/components/breakout-room/ui/BreakoutRoomParticipants.tsx +0 -124
  105. package/template/src/components/breakout-room/ui/BreakoutRoomRaiseHand.tsx +0 -65
  106. package/template/src/components/breakout-room/ui/BreakoutRoomRenameModal.tsx +0 -227
  107. package/template/src/components/breakout-room/ui/BreakoutRoomSettings.tsx +0 -140
  108. package/template/src/components/breakout-room/ui/BreakoutRoomTransition.tsx +0 -52
  109. package/template/src/components/breakout-room/ui/BreakoutRoomView.tsx +0 -193
  110. package/template/src/components/breakout-room/ui/ExitBreakoutRoomIconButton.tsx +0 -79
  111. package/template/src/components/breakout-room/ui/ParticipantManualAssignmentModal.tsx +0 -638
  112. package/template/src/components/breakout-room/ui/SelectParticipantAssignmentStrategy.tsx +0 -57
  113. package/template/src/components/common/Dividers.tsx +0 -53
  114. package/template/src/components/controls/toolbar-items/ExitBreakoutRoomToolbarItem.tsx +0 -13
  115. package/template/src/components/raise-hand/RaiseHandButton.tsx +0 -50
  116. package/template/src/components/raise-hand/RaiseHandProvider.tsx +0 -308
  117. package/template/src/components/raise-hand/index.ts +0 -14
  118. package/template/src/components/room-info/useCurrentRoomInfo.tsx +0 -42
  119. package/template/src/components/room-info/useSetBreakoutRoomInfo.tsx +0 -64
  120. package/template/src/pages/video-call/BreakoutVideoCall.tsx +0 -213
  121. package/template/src/pages/video-call/VideoCallContent.tsx +0 -211
  122. package/template/src/pages/video-call/VideoCallStateWrapper.tsx +0 -495
  123. package/template/src/rtm/RTMConfigureBreakoutRoomProvider.tsx +0 -882
  124. package/template/src/rtm/RTMConfigureMainRoomProvider.tsx +0 -757
  125. package/template/src/rtm/RTMCoreProvider.tsx +0 -419
  126. package/template/src/rtm/RTMGlobalStateProvider.tsx +0 -706
  127. package/template/src/rtm/RTMStatusBanner.tsx +0 -99
  128. package/template/src/rtm/constants.ts +0 -12
  129. package/template/src/rtm/hooks/useMainRoomUserDisplayName.ts +0 -45
  130. package/template/src/rtm/rtm-presence-utils.ts +0 -344
  131. package/template/src/subComponents/chat/ChatAnnouncementView.tsx +0 -65
  132. package/template/src/utils/useDebouncedCallback.tsx +0 -20
@@ -1,502 +1,540 @@
1
+ /*
2
+ ********************************************
3
+ Copyright © 2021 Agora Lab, Inc., all rights reserved.
4
+ AppBuilder and all associated components, source code, APIs, services, and documentation
5
+ (the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
6
+ accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
7
+ Use without a license or in violation of any license terms and conditions (including use for
8
+ any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
9
+ information visit https://appbuilder.agora.io.
10
+ *********************************************
11
+ */
12
+ // @ts-nocheck
1
13
  import {
2
- type Metadata as NativeMetadata,
3
- type MetadataItem as NativeMetadataItem,
4
- type GetUserMetadataOptions as NativeGetUserMetadataOptions,
5
- type RtmChannelType as NativeRtmChannelType,
6
- type SetUserMetadataResponse,
7
- type LoginOptions as NativeLoginOptions,
8
- type RTMClientEventMap as NativeRTMClientEventMap,
9
- type GetUserMetadataResponse as NativeGetUserMetadataResponse,
10
- type GetChannelMetadataResponse as NativeGetChannelMetadataResponse,
11
- type SetOrUpdateUserMetadataOptions as NativeSetOrUpdateUserMetadataOptions,
12
- type RemoveUserMetadataOptions as NativeRemoveUserMetadataOptions,
13
- type RemoveUserMetadataResponse as NativeRemoveUserMetadataResponse,
14
- type IMetadataOptions as NativeIMetadataOptions,
15
- type StorageEvent as NativeStorageEvent,
16
- type PresenceEvent as NativePresenceEvent,
17
- type MessageEvent as NativeMessageEvent,
18
- type SubscribeOptions as NativeSubscribeOptions,
19
- type PublishOptions as NativePublishOptions,
20
- } from 'agora-react-native-rtm';
21
- import AgoraRTM, {
22
- RTMClient,
23
- GetUserMetadataResponse,
24
- GetChannelMetadataResponse,
25
- PublishOptions,
26
- ChannelType,
27
- MetaDataDetail,
28
- RemoveUserMetadataOptions,
29
- } from 'agora-rtm-sdk';
30
- import {
31
- linkStatusReasonCodeMapping,
32
- nativeChannelTypeMapping,
33
- nativeLinkStateMapping,
34
- nativeMessageEventTypeMapping,
35
- nativePresenceEventTypeMapping,
36
- nativeStorageEventTypeMapping,
37
- nativeStorageTypeMapping,
38
- webChannelTypeMapping,
39
- } from './Types';
40
-
41
- type CallbackType = (args?: any) => void;
42
-
43
- // Conversion function
44
- const convertWebToNativeMetadata = (webMetadata: any): NativeMetadata => {
45
- // Convert object entries to MetadataItem array
46
- const items: NativeMetadataItem[] =
47
- Object.entries(webMetadata.metadata).map(
48
- ([key, metadataItem]: [string, MetaDataDetail]) => {
49
- return {
50
- key: key,
51
- value: metadataItem.value,
52
- revision: metadataItem.revision,
53
- authorUserId: metadataItem.authorUid,
54
- updateTs: metadataItem.updated,
55
- };
56
- },
57
- ) || [];
58
-
59
- // Create native Metadata object
60
- const nativeMetadata: NativeMetadata = {
61
- majorRevision: webMetadata?.revision || -1, // Use first item's revision as major revision
62
- items: items,
63
- itemCount: webMetadata?.totalCount || 0,
64
- };
65
-
66
- return nativeMetadata;
67
- };
68
-
69
- export class RTMWebClient {
70
- private client: RTMClient;
71
- private appId: string;
72
- private userId: string;
73
- private eventsMap = new Map<keyof NativeRTMClientEventMap, CallbackType>([
74
- ['linkState', () => null],
75
- ['storage', () => null],
76
- ['presence', () => null],
77
- ['message', () => null],
14
+ ChannelAttributeOptions,
15
+ RtmAttribute,
16
+ RtmChannelAttribute,
17
+ Subscription,
18
+ } from 'agora-react-native-rtm/lib/typescript/src';
19
+ import {RtmClientEvents} from 'agora-react-native-rtm/lib/typescript/src/RtmEngine';
20
+ import AgoraRTM, {VERSION} from 'agora-rtm-sdk';
21
+ import RtmClient from 'agora-react-native-rtm';
22
+ import {LogSource, logger} from '../../../src/logger/AppBuilderLogger';
23
+ // export {RtmAttribute}
24
+ //
25
+ interface RtmAttributePlaceholder {}
26
+ export {RtmAttributePlaceholder as RtmAttribute};
27
+
28
+ type callbackType = (args?: any) => void;
29
+
30
+ export default class RtmEngine {
31
+ public appId: string;
32
+ public client: RtmClient;
33
+ public channelMap = new Map<string, any>([]);
34
+ public remoteInvititations = new Map<string, any>([]);
35
+ public localInvititations = new Map<string, any>([]);
36
+ public channelEventsMap = new Map<string, any>([
37
+ ['channelMessageReceived', () => null],
38
+ ['channelMemberJoined', () => null],
39
+ ['channelMemberLeft', () => null],
40
+ ]);
41
+ public clientEventsMap = new Map<string, any>([
42
+ ['connectionStateChanged', () => null],
43
+ ['messageReceived', () => null],
44
+ ['remoteInvitationReceived', () => null],
45
+ ['tokenExpired', () => null],
78
46
  ]);
47
+ public localInvitationEventsMap = new Map<string, any>([
48
+ ['localInvitationAccepted', () => null],
49
+ ['localInvitationCanceled', () => null],
50
+ ['localInvitationFailure', () => null],
51
+ ['localInvitationReceivedByPeer', () => null],
52
+ ['localInvitationRefused', () => null],
53
+ ]);
54
+ public remoteInvitationEventsMap = new Map<string, any>([
55
+ ['remoteInvitationAccepted', () => null],
56
+ ['remoteInvitationCanceled', () => null],
57
+ ['remoteInvitationFailure', () => null],
58
+ ['remoteInvitationRefused', () => null],
59
+ ]);
60
+ constructor() {
61
+ this.appId = '';
62
+ logger.debug(LogSource.AgoraSDK, 'Log', 'Using RTM Bridge');
63
+ }
79
64
 
80
- constructor(appId: string, userId: string) {
81
- this.appId = appId;
82
- this.userId = `${userId}`;
83
- try {
84
- // Create the actual web RTM client
85
- this.client = new AgoraRTM.RTM(this.appId, this.userId);
86
-
87
- this.client.addEventListener('linkState', data => {
88
- const nativeState = {
89
- ...data,
90
- currentState:
91
- nativeLinkStateMapping[data.currentState] ||
92
- nativeLinkStateMapping.IDLE,
93
- previousState:
94
- nativeLinkStateMapping[data.previousState] ||
95
- nativeLinkStateMapping.IDLE,
96
- reasonCode: linkStatusReasonCodeMapping[data.reasonCode] || 0,
97
- };
98
- (this.eventsMap.get('linkState') ?? (() => {}))(nativeState);
99
- });
65
+ on(event: any, listener: any) {
66
+ if (
67
+ event === 'channelMessageReceived' ||
68
+ event === 'channelMemberJoined' ||
69
+ event === 'channelMemberLeft'
70
+ ) {
71
+ this.channelEventsMap.set(event, listener);
72
+ } else if (
73
+ event === 'connectionStateChanged' ||
74
+ event === 'messageReceived' ||
75
+ event === 'remoteInvitationReceived' ||
76
+ event === 'tokenExpired'
77
+ ) {
78
+ this.clientEventsMap.set(event, listener);
79
+ } else if (
80
+ event === 'localInvitationAccepted' ||
81
+ event === 'localInvitationCanceled' ||
82
+ event === 'localInvitationFailure' ||
83
+ event === 'localInvitationReceivedByPeer' ||
84
+ event === 'localInvitationRefused'
85
+ ) {
86
+ this.localInvitationEventsMap.set(event, listener);
87
+ } else if (
88
+ event === 'remoteInvitationAccepted' ||
89
+ event === 'remoteInvitationCanceled' ||
90
+ event === 'remoteInvitationFailure' ||
91
+ event === 'remoteInvitationRefused'
92
+ ) {
93
+ this.remoteInvitationEventsMap.set(event, listener);
94
+ }
95
+ }
100
96
 
101
- this.client.addEventListener('storage', data => {
102
- const nativeStorageEvent: NativeStorageEvent = {
103
- channelType: nativeChannelTypeMapping[data.channelType],
104
- storageType: nativeStorageTypeMapping[data.storageType],
105
- eventType: nativeStorageEventTypeMapping[data.eventType],
106
- data: convertWebToNativeMetadata(data.data),
107
- timestamp: data.timestamp,
108
- };
109
- (this.eventsMap.get('storage') ?? (() => {}))(nativeStorageEvent);
97
+ createClient(APP_ID: string) {
98
+ this.appId = APP_ID;
99
+ this.client = AgoraRTM.createInstance(this.appId);
100
+
101
+ if ($config.GEO_FENCING) {
102
+ try {
103
+ //include area is comma seperated value
104
+ let includeArea = $config.GEO_FENCING_INCLUDE_AREA
105
+ ? $config.GEO_FENCING_INCLUDE_AREA
106
+ : AREAS.GLOBAL;
107
+
108
+ //exclude area is single value
109
+ let excludeArea = $config.GEO_FENCING_EXCLUDE_AREA
110
+ ? $config.GEO_FENCING_EXCLUDE_AREA
111
+ : '';
112
+
113
+ includeArea = includeArea?.split(',');
114
+
115
+ //pass excludedArea if only its provided
116
+ if (excludeArea) {
117
+ AgoraRTM.setArea({
118
+ areaCodes: includeArea,
119
+ excludedArea: excludeArea,
120
+ });
121
+ }
122
+ //otherwise we can pass area directly
123
+ else {
124
+ AgoraRTM.setArea({areaCodes: includeArea});
125
+ }
126
+ } catch (setAeraError) {
127
+ console.log('error on RTM setArea', setAeraError);
128
+ }
129
+ }
130
+
131
+ window.rtmClient = this.client;
132
+
133
+ this.client.on('ConnectionStateChanged', (state, reason) => {
134
+ this.clientEventsMap.get('connectionStateChanged')({state, reason});
135
+ });
136
+
137
+ this.client.on('MessageFromPeer', (msg, uid, msgProps) => {
138
+ this.clientEventsMap.get('messageReceived')({
139
+ text: msg.text,
140
+ ts: msgProps.serverReceivedTs,
141
+ offline: msgProps.isOfflineMessage,
142
+ peerId: uid,
143
+ });
144
+ });
145
+
146
+ this.client.on('RemoteInvitationReceived', (remoteInvitation: any) => {
147
+ this.remoteInvititations.set(remoteInvitation.callerId, remoteInvitation);
148
+ this.clientEventsMap.get('remoteInvitationReceived')({
149
+ callerId: remoteInvitation.callerId,
150
+ content: remoteInvitation.content,
151
+ state: remoteInvitation.state,
152
+ channelId: remoteInvitation.channelId,
153
+ response: remoteInvitation.response,
110
154
  });
111
155
 
112
- this.client.addEventListener('presence', data => {
113
- const nativePresenceEvent: NativePresenceEvent = {
114
- channelName: data.channelName,
115
- channelType: nativeChannelTypeMapping[data.channelType],
116
- type: nativePresenceEventTypeMapping[data.eventType],
117
- publisher: data.publisher,
118
- timestamp: data.timestamp,
119
- };
120
- (this.eventsMap.get('presence') ?? (() => {}))(nativePresenceEvent);
156
+ remoteInvitation.on('RemoteInvitationAccepted', () => {
157
+ this.remoteInvitationEventsMap.get('RemoteInvitationAccepted')({
158
+ callerId: remoteInvitation.callerId,
159
+ content: remoteInvitation.content,
160
+ state: remoteInvitation.state,
161
+ channelId: remoteInvitation.channelId,
162
+ response: remoteInvitation.response,
163
+ });
121
164
  });
122
165
 
123
- this.client.addEventListener('message', data => {
124
- const nativeMessageEvent: NativeMessageEvent = {
125
- ...data,
126
- channelType: nativeChannelTypeMapping[data.channelType],
127
- messageType: nativeMessageEventTypeMapping[data.messageType],
128
- message: `${data.message}`,
129
- };
130
- (this.eventsMap.get('message') ?? (() => {}))(nativeMessageEvent);
166
+ remoteInvitation.on('RemoteInvitationCanceled', (content: string) => {
167
+ this.remoteInvitationEventsMap.get('remoteInvitationCanceled')({
168
+ callerId: remoteInvitation.callerId,
169
+ content: content,
170
+ state: remoteInvitation.state,
171
+ channelId: remoteInvitation.channelId,
172
+ response: remoteInvitation.response,
173
+ });
131
174
  });
132
- } catch (error) {
133
- const contextError = new Error(
134
- `Failed to create RTMWebClient for appId: ${this.appId}, userId: ${
135
- this.userId
136
- }. Error: ${error.message || error}`,
137
- );
138
- console.error('RTMWebClient constructor error:', contextError);
139
- throw contextError;
140
- }
141
- }
142
175
 
143
- // Storage methods
144
- get storage() {
145
- return {
146
- setUserMetadata: (
147
- data: NativeMetadata,
148
- options?: NativeSetOrUpdateUserMetadataOptions,
149
- ): Promise<SetUserMetadataResponse> => {
150
- // 1. Validate input parameters
151
- if (!data) {
152
- throw new Error('setUserMetadata: data parameter is required');
153
- }
154
- if (!data.items || !Array.isArray(data.items)) {
155
- throw new Error(
156
- 'setUserMetadata: data.items must be a non-empty array',
157
- );
158
- }
159
- if (data.items.length === 0) {
160
- throw new Error('setUserMetadata: data.items cannot be empty');
161
- }
162
- // 2. Make sure key is present as this is mandatory
163
- // https://docs.agora.io/en/signaling/reference/api?platform=web#storagesetuserpropsag_platform
164
- const validatedItems = data.items.map((item, index) => {
165
- if (!item.key || typeof item.key !== 'string') {
166
- throw new Error(
167
- `setUserMetadata: item at index ${index} missing required 'key' property`,
168
- );
169
- }
170
- return {
171
- key: item.key,
172
- value: item.value || '', // Default to empty string if not provided
173
- revision: item.revision || -1, // Default to -1 if not provided
174
- };
176
+ remoteInvitation.on('RemoteInvitationFailure', (reason: string) => {
177
+ this.remoteInvitationEventsMap.get('remoteInvitationFailure')({
178
+ callerId: remoteInvitation.callerId,
179
+ content: remoteInvitation.content,
180
+ state: remoteInvitation.state,
181
+ channelId: remoteInvitation.channelId,
182
+ response: remoteInvitation.response,
183
+ code: -1, //Web sends string, RN expect number but can't find enum
175
184
  });
176
- // Map native signature to web signature
177
- return this.client.storage.setUserMetadata(validatedItems, {
178
- addTimeStamp: options?.addTimeStamp || true,
179
- addUserId: options?.addUserId || true,
185
+ });
186
+
187
+ remoteInvitation.on('RemoteInvitationRefused', () => {
188
+ this.remoteInvitationEventsMap.get('remoteInvitationRefused')({
189
+ callerId: remoteInvitation.callerId,
190
+ content: remoteInvitation.content,
191
+ state: remoteInvitation.state,
192
+ channelId: remoteInvitation.channelId,
193
+ response: remoteInvitation.response,
180
194
  });
181
- },
195
+ });
196
+ });
182
197
 
183
- getUserMetadata: async (options: NativeGetUserMetadataOptions) => {
184
- // Validate input parameters
185
- if (!options) {
186
- throw new Error('getUserMetadata: options parameter is required');
187
- }
188
- if (
189
- !options.userId ||
190
- typeof options.userId !== 'string' ||
191
- options.userId.trim() === ''
192
- ) {
193
- throw new Error(
194
- 'getUserMetadata: options.userId must be a non-empty string',
195
- );
196
- }
197
- const webResponse: GetUserMetadataResponse =
198
- await this.client.storage.getUserMetadata({
199
- userId: options.userId,
200
- });
198
+ this.client.on('TokenExpired', () => {
199
+ this.clientEventsMap.get('tokenExpired')({}); //RN expect evt: any
200
+ });
201
+ }
202
+
203
+ async login(loginParam: {uid: string; token?: string}): Promise<any> {
204
+ return this.client.login(loginParam);
205
+ }
206
+
207
+ async logout(): Promise<any> {
208
+ return await this.client.logout();
209
+ }
210
+
211
+ async joinChannel(channelId: string): Promise<any> {
212
+ this.channelMap.set(channelId, this.client.createChannel(channelId));
213
+ this.channelMap
214
+ .get(channelId)
215
+ .on('ChannelMessage', (msg: {text: string}, uid: string, messagePros) => {
216
+ let text = msg.text;
217
+ let ts = messagePros.serverReceivedTs;
218
+ this.channelEventsMap.get('channelMessageReceived')({
219
+ uid,
220
+ channelId,
221
+ text,
222
+ ts,
223
+ });
224
+ });
225
+ this.channelMap.get(channelId).on('MemberJoined', (uid: string) => {
226
+ this.channelEventsMap.get('channelMemberJoined')({uid, channelId});
227
+ });
228
+ this.channelMap.get(channelId).on('MemberLeft', (uid: string) => {
229
+ console.log('Member Left', this.channelEventsMap);
230
+ this.channelEventsMap.get('channelMemberLeft')({uid});
231
+ });
232
+ this.channelMap
233
+ .get(channelId)
234
+ .on('AttributesUpdated', (attributes: RtmChannelAttribute) => {
201
235
  /**
202
- * majorRevision : 13483783553
203
- * metadata :
204
- * {
205
- * isHost: {authorUid: "", revision: 13483783553, updated: 0, value : "true"},
206
- * screenUid: {…}}
207
- * }
208
- * timestamp: 0
209
- * totalCount: 2
210
- * userId: "xxx"
236
+ * a) Kindly note the below event listener 'channelAttributesUpdated' expects type
237
+ * RtmChannelAttribute[] (array of objects [{key: 'valueOfKey', value: 'valueOfValue}])
238
+ * whereas the above listener 'AttributesUpdated' receives attributes in object form
239
+ * {[valueOfKey]: valueOfValue} of type RtmChannelAttribute
240
+ * b) Hence in this bridge the data should be modified to keep in sync with both the
241
+ * listeners for web and listener for native
211
242
  */
212
- const items = Object.entries(webResponse.metadata).map(
213
- ([key, metadataItem]) => ({
214
- key: key,
215
- value: metadataItem.value,
216
- }),
243
+ /**
244
+ * 1. Loop through object
245
+ * 2. Create a object {key: "", value: ""} and push into array
246
+ * 3. Return the Array
247
+ */
248
+ const channelAttributes = Object.keys(attributes).reduce((acc, key) => {
249
+ const {value, lastUpdateTs, lastUpdateUserId} = attributes[key];
250
+ acc.push({key, value, lastUpdateTs, lastUpdateUserId});
251
+ return acc;
252
+ }, []);
253
+
254
+ this.channelEventsMap.get('ChannelAttributesUpdated')(
255
+ channelAttributes,
217
256
  );
218
- const nativeResponse: NativeGetUserMetadataResponse = {
219
- items: [...items],
220
- itemCount: webResponse.totalCount,
221
- userId: webResponse.userId,
222
- timestamp: webResponse.timestamp,
223
- };
224
- return nativeResponse;
225
- },
257
+ });
226
258
 
227
- removeUserMetadata: async (
228
- options?: NativeRemoveUserMetadataOptions,
229
- ): Promise<NativeRemoveUserMetadataResponse> => {
230
- // Build the options object for the web SDK call
231
- const webOptions: RemoveUserMetadataOptions = {};
259
+ return this.channelMap.get(channelId).join();
260
+ }
232
261
 
233
- // Add userId if provided (for removing another user's metadata, defaults to self if not provided)
234
- if (options?.userId && typeof options.userId === 'string') {
235
- webOptions.userId = options.userId;
236
- }
262
+ async leaveChannel(channelId: string): Promise<any> {
263
+ if (this.channelMap.get(channelId)) {
264
+ return this.channelMap.get(channelId).leave();
265
+ } else {
266
+ Promise.reject('Wrong channel');
267
+ }
268
+ }
237
269
 
238
- // Convert native Metadata to web MetadataItem[] format if provided
239
- if (
240
- options?.data &&
241
- options.data.items &&
242
- Array.isArray(options.data.items) &&
243
- options.data.items.length > 0
244
- ) {
245
- webOptions.data = options.data.items.map(item => ({
246
- key: item.key,
247
- value: item.value || '', // Require not used for remove.we use keys
248
- }));
249
- }
270
+ async sendMessageByChannelId(channel: string, message: string): Promise<any> {
271
+ if (this.channelMap.get(channel)) {
272
+ return this.channelMap.get(channel).sendMessage({text: message});
273
+ } else {
274
+ console.log(this.channelMap, channel);
275
+ Promise.reject('Wrong channel');
276
+ }
277
+ }
250
278
 
251
- return await this.client.storage.removeUserMetadata(webOptions);
252
- },
279
+ destroyClient() {
280
+ console.log('Destroy called');
281
+ this.channelEventsMap.forEach((callback, event) => {
282
+ this.client.off(event, callback);
283
+ });
284
+ this.channelEventsMap.clear();
285
+ this.channelMap.clear();
286
+ this.clientEventsMap.clear();
287
+ this.remoteInvitationEventsMap.clear();
288
+ this.localInvitationEventsMap.clear();
289
+ }
253
290
 
254
- setChannelMetadata: async (
255
- channelName: string,
256
- channelType: NativeRtmChannelType,
257
- data: NativeMetadata,
258
- options?: NativeIMetadataOptions,
259
- ) => {
260
- // Validate input parameters
261
- if (
262
- !channelName ||
263
- typeof channelName !== 'string' ||
264
- channelName.trim() === ''
265
- ) {
266
- throw new Error(
267
- 'setChannelMetadata: channelName must be a non-empty string',
268
- );
269
- }
270
- if (typeof channelType !== 'number') {
271
- throw new Error('setChannelMetadata: channelType must be a number');
272
- }
273
- if (!data) {
274
- throw new Error('setChannelMetadata: data parameter is required');
275
- }
276
- if (!data.items || !Array.isArray(data.items)) {
277
- throw new Error('setChannelMetadata: data.items must be an array');
278
- }
279
- if (data.items.length === 0) {
280
- throw new Error('setChannelMetadata: data.items cannot be empty');
281
- }
282
- // 2. Make sure key is present as this is mandatory
283
- // https://docs.agora.io/en/signaling/reference/api?platform=web#storagesetuserpropsag_platform
284
- const validatedItems = data.items.map((item, index) => {
285
- if (!item.key || typeof item.key !== 'string') {
286
- throw new Error(
287
- `setChannelMetadata: item at index ${index} missing required 'key' property`,
288
- );
289
- }
290
- return {
291
- key: item.key,
292
- value: item.value || '', // Default to empty string if not provided
293
- revision: item.revision || -1, // Default to -1 if not provided
294
- };
291
+ async getChannelMembersBychannelId(channel: string) {
292
+ if (this.channelMap.get(channel)) {
293
+ let memberArray: Array<any> = [];
294
+ let currentChannel = this.channelMap.get(channel);
295
+ await currentChannel.getMembers().then((arr: Array<number>) => {
296
+ arr.map((elem: number) => {
297
+ memberArray.push({
298
+ channelId: channel,
299
+ uid: elem,
300
+ });
295
301
  });
296
- return this.client.storage.setChannelMetadata(
297
- channelName,
298
- (webChannelTypeMapping[channelType] as ChannelType) || 'MESSAGE',
299
- validatedItems,
300
- {
301
- addUserId: options?.addUserId || true,
302
- addTimeStamp: options?.addTimeStamp || true,
303
- },
304
- );
305
- },
302
+ });
303
+ return {members: memberArray};
304
+ } else {
305
+ Promise.reject('Wrong channel');
306
+ }
307
+ }
306
308
 
307
- getChannelMetadata: async (
308
- channelName: string,
309
- channelType: NativeRtmChannelType,
310
- ) => {
311
- try {
312
- const webResponse: GetChannelMetadataResponse =
313
- await this.client.storage.getChannelMetadata(
314
- channelName,
315
- (webChannelTypeMapping[channelType] as ChannelType) || 'MESSAGE',
316
- );
317
-
318
- const items = Object.entries(webResponse.metadata).map(
319
- ([key, metadataItem]) => ({
320
- key: key,
321
- value: metadataItem.value,
322
- }),
323
- );
324
- const nativeResponse: NativeGetChannelMetadataResponse = {
325
- items: [...items],
326
- itemCount: webResponse.totalCount,
327
- timestamp: webResponse.timestamp,
328
- channelName: webResponse.channelName,
329
- channelType: nativeChannelTypeMapping.MESSAGE,
330
- };
331
- return nativeResponse;
332
- } catch (error) {
333
- const contextError = new Error(
334
- `Failed to get channel metadata for channel '${channelName}' with type ${channelType}: ${
335
- error.message || error
336
- }`,
337
- );
338
- console.error('BRIDGE getChannelMetadata error:', contextError);
339
- throw contextError;
340
- }
341
- },
342
- };
309
+ async queryPeersOnlineStatus(uid: Array<String>) {
310
+ let peerArray: Array<any> = [];
311
+ await this.client.queryPeersOnlineStatus(uid).then(list => {
312
+ Object.entries(list).forEach(value => {
313
+ peerArray.push({
314
+ online: value[1],
315
+ uid: value[0],
316
+ });
317
+ });
318
+ });
319
+ return {items: peerArray};
343
320
  }
344
321
 
345
- get presence() {
346
- return {
347
- getOnlineUsers: async (
348
- channelName: string,
349
- channelType: NativeRtmChannelType,
350
- ) => {
351
- // Validate input parameters
352
- if (
353
- !channelName ||
354
- typeof channelName !== 'string' ||
355
- channelName.trim() === ''
356
- ) {
357
- throw new Error(
358
- 'getOnlineUsers: channelName must be a non-empty string',
359
- );
360
- }
361
- if (typeof channelType !== 'number') {
362
- throw new Error('getOnlineUsers: channelType must be a number');
363
- }
322
+ async renewToken(token: string) {
323
+ return this.client.renewToken(token);
324
+ }
364
325
 
365
- try {
366
- // Call web SDK's presence method
367
- const result = await this.client.presence.getOnlineUsers(
368
- channelName,
369
- (webChannelTypeMapping[channelType] as ChannelType) || 'MESSAGE',
370
- );
371
- return result;
372
- } catch (error) {
373
- const contextError = new Error(
374
- `Failed to get online users for channel '${channelName}' with type ${channelType}: ${
375
- error.message || error
376
- }`,
377
- );
378
- console.error('BRIDGE presence error:', contextError);
379
- throw contextError;
380
- }
381
- },
326
+ async getUserAttributesByUid(uid: string) {
327
+ let response = {};
328
+ await this.client
329
+ .getUserAttributes(uid)
330
+ .then((attributes: string) => {
331
+ response = {attributes, uid};
332
+ })
333
+ .catch((e: any) => {
334
+ Promise.reject(e);
335
+ });
336
+ return response;
337
+ }
382
338
 
383
- whoNow: async (
384
- channelName: string,
385
- channelType?: NativeRtmChannelType,
386
- ) => {
387
- const webChannelType = channelType
388
- ? (webChannelTypeMapping[channelType] as ChannelType)
389
- : 'MESSAGE';
390
- return this.client.presence.whoNow(channelName, webChannelType);
391
- },
339
+ async getChannelAttributes(channelId: string) {
340
+ let response = {};
341
+ await this.client
342
+ .getChannelAttributes(channelId)
343
+ .then((attributes: RtmChannelAttribute) => {
344
+ /**
345
+ * Here the attributes received are in the format {[valueOfKey]: valueOfValue} of type RtmChannelAttribute
346
+ * We need to convert it into (array of objects [{key: 'valueOfKey', value: 'valueOfValue}])
347
+ /**
348
+ * 1. Loop through object
349
+ * 2. Create a object {key: "", value: ""} and push into array
350
+ * 3. Return the Array
351
+ */
352
+ const channelAttributes = Object.keys(attributes).reduce((acc, key) => {
353
+ const {value, lastUpdateTs, lastUpdateUserId} = attributes[key];
354
+ acc.push({key, value, lastUpdateTs, lastUpdateUserId});
355
+ return acc;
356
+ }, []);
357
+ response = channelAttributes;
358
+ })
359
+ .catch((e: any) => {
360
+ Promise.reject(e);
361
+ });
362
+ return response;
363
+ }
392
364
 
393
- whereNow: async (userId: string) => {
394
- return this.client.presence.whereNow(userId);
395
- },
396
- };
365
+ async removeAllLocalUserAttributes() {
366
+ return this.client.clearLocalUserAttributes();
397
367
  }
398
368
 
399
- addEventListener(
400
- event: keyof NativeRTMClientEventMap,
401
- listener: (event: any) => void,
402
- ) {
403
- if (this.client) {
404
- // Simply replace the handler in our map - web client listeners are fixed in constructor
405
- this.eventsMap.set(event, listener as CallbackType);
406
- }
369
+ async removeLocalUserAttributesByKeys(keys: string[]) {
370
+ return this.client.deleteLocalUserAttributesByKeys(keys);
407
371
  }
408
372
 
409
- removeEventListener(
410
- event: keyof NativeRTMClientEventMap,
411
- _listener: (event: any) => void,
412
- ) {
413
- if (this.client && this.eventsMap.has(event)) {
414
- const prevListener = this.eventsMap.get(event);
415
- if (prevListener) {
416
- this.client.removeEventListener(event, prevListener);
417
- }
418
- this.eventsMap.set(event, () => null); // reset to no-op
419
- }
373
+ async replaceLocalUserAttributes(attributes: string[]) {
374
+ let formattedAttributes: any = {};
375
+ attributes.map(attribute => {
376
+ let key = Object.values(attribute)[0];
377
+ let value = Object.values(attribute)[1];
378
+ formattedAttributes[key] = value;
379
+ });
380
+ return this.client.setLocalUserAttributes({...formattedAttributes});
420
381
  }
421
382
 
422
- // Core RTM methods - direct delegation to web SDK
423
- async login(options?: NativeLoginOptions) {
424
- if (!options?.token) {
425
- throw new Error('login: token is required in options');
426
- }
427
- return this.client.login({token: options.token});
383
+ async setLocalUserAttributes(attributes: string[]) {
384
+ let formattedAttributes: any = {};
385
+ attributes.map(attribute => {
386
+ let key = Object.values(attribute)[0];
387
+ let value = Object.values(attribute)[1];
388
+ formattedAttributes[key] = value;
389
+ // console.log('!!!!formattedAttributes', formattedAttributes, key, value);
390
+ });
391
+ return this.client.setLocalUserAttributes({...formattedAttributes});
428
392
  }
429
393
 
430
- async logout() {
431
- return this.client.logout();
394
+ async addOrUpdateLocalUserAttributes(attributes: RtmAttribute[]) {
395
+ let formattedAttributes: any = {};
396
+ attributes.map(attribute => {
397
+ let key = Object.values(attribute)[0];
398
+ let value = Object.values(attribute)[1];
399
+ formattedAttributes[key] = value;
400
+ });
401
+ return this.client.addOrUpdateLocalUserAttributes({...formattedAttributes});
432
402
  }
433
403
 
434
- async subscribe(channelName: string, options?: NativeSubscribeOptions) {
435
- if (
436
- !channelName ||
437
- typeof channelName !== 'string' ||
438
- channelName.trim() === ''
439
- ) {
440
- throw new Error('subscribe: channelName must be a non-empty string');
441
- }
442
- return this.client.subscribe(channelName, options);
404
+ async addOrUpdateChannelAttributes(
405
+ channelId: string,
406
+ attributes: RtmChannelAttribute[],
407
+ option: ChannelAttributeOptions,
408
+ ): Promise<void> {
409
+ let formattedAttributes: any = {};
410
+ attributes.map(attribute => {
411
+ let key = Object.values(attribute)[0];
412
+ let value = Object.values(attribute)[1];
413
+ formattedAttributes[key] = value;
414
+ });
415
+ return this.client.addOrUpdateChannelAttributes(
416
+ channelId,
417
+ {...formattedAttributes},
418
+ option,
419
+ );
443
420
  }
444
421
 
445
- async unsubscribe(channelName: string) {
446
- return this.client.unsubscribe(channelName);
422
+ async sendLocalInvitation(invitationProps: any) {
423
+ let invite = this.client.createLocalInvitation(invitationProps.uid);
424
+ this.localInvititations.set(invitationProps.uid, invite);
425
+ invite.content = invitationProps.content;
426
+
427
+ invite.on('LocalInvitationAccepted', (response: string) => {
428
+ this.localInvitationEventsMap.get('localInvitationAccepted')({
429
+ calleeId: invite.calleeId,
430
+ content: invite.content,
431
+ state: invite.state,
432
+ channelId: invite.channelId,
433
+ response,
434
+ });
435
+ });
436
+
437
+ invite.on('LocalInvitationCanceled', () => {
438
+ this.localInvitationEventsMap.get('localInvitationCanceled')({
439
+ calleeId: invite.calleeId,
440
+ content: invite.content,
441
+ state: invite.state,
442
+ channelId: invite.channelId,
443
+ response: invite.response,
444
+ });
445
+ });
446
+
447
+ invite.on('LocalInvitationFailure', (reason: string) => {
448
+ this.localInvitationEventsMap.get('localInvitationFailure')({
449
+ calleeId: invite.calleeId,
450
+ content: invite.content,
451
+ state: invite.state,
452
+ channelId: invite.channelId,
453
+ response: invite.response,
454
+ code: -1, //Web sends string, RN expect number but can't find enum
455
+ });
456
+ });
457
+
458
+ invite.on('LocalInvitationReceivedByPeer', () => {
459
+ this.localInvitationEventsMap.get('localInvitationReceivedByPeer')({
460
+ calleeId: invite.calleeId,
461
+ content: invite.content,
462
+ state: invite.state,
463
+ channelId: invite.channelId,
464
+ response: invite.response,
465
+ });
466
+ });
467
+
468
+ invite.on('LocalInvitationRefused', (response: string) => {
469
+ this.localInvitationEventsMap.get('localInvitationRefused')({
470
+ calleeId: invite.calleeId,
471
+ content: invite.content,
472
+ state: invite.state,
473
+ channelId: invite.channelId,
474
+ response: response,
475
+ });
476
+ });
477
+ return invite.send();
447
478
  }
448
479
 
449
- async publish(
450
- channelName: string,
451
- message: string,
452
- options?: NativePublishOptions,
453
- ) {
454
- // Validate input parameters
455
- if (
456
- !channelName ||
457
- typeof channelName !== 'string' ||
458
- channelName.trim() === ''
459
- ) {
460
- throw new Error('publish: channelName must be a non-empty string');
461
- }
462
- if (typeof message !== 'string') {
463
- throw new Error('publish: message must be a string');
464
- }
480
+ async sendMessageToPeer(AgoraPeerMessage: {
481
+ peerId: string;
482
+ offline: boolean;
483
+ text: string;
484
+ }) {
485
+ return this.client.sendMessageToPeer(
486
+ {text: AgoraPeerMessage.text},
487
+ AgoraPeerMessage.peerId,
488
+ );
489
+ //check promise result
490
+ }
465
491
 
466
- const webOptions: PublishOptions = {
467
- ...options,
468
- channelType:
469
- (webChannelTypeMapping[options?.channelType] as ChannelType) ||
470
- 'MESSAGE',
471
- };
472
- return this.client.publish(channelName, message, webOptions);
492
+ async acceptRemoteInvitation(remoteInvitationProps: {
493
+ uid: string;
494
+ response?: string;
495
+ channelId: string;
496
+ }) {
497
+ let invite = this.remoteInvititations.get(remoteInvitationProps.uid);
498
+ // console.log(invite);
499
+ // console.log(this.remoteInvititations);
500
+ // console.log(remoteInvitationProps.uid);
501
+ return invite.accept();
473
502
  }
474
503
 
475
- async renewToken(token: string) {
476
- return this.client.renewToken(token);
504
+ async refuseRemoteInvitation(remoteInvitationProps: {
505
+ uid: string;
506
+ response?: string;
507
+ channelId: string;
508
+ }) {
509
+ return this.remoteInvititations.get(remoteInvitationProps.uid).refuse();
477
510
  }
478
511
 
479
- removeAllListeners() {
480
- this.eventsMap = new Map([
481
- ['linkState', () => null],
482
- ['storage', () => null],
483
- ['presence', () => null],
484
- ['message', () => null],
485
- ]);
486
- return this.client.removeAllListeners();
512
+ async cancelLocalInvitation(LocalInvitationProps: {
513
+ uid: string;
514
+ content?: string;
515
+ channelId?: string;
516
+ }) {
517
+ console.log(this.localInvititations.get(LocalInvitationProps.uid));
518
+ return this.localInvititations.get(LocalInvitationProps.uid).cancel();
487
519
  }
488
- }
489
520
 
490
- export class RtmConfig {
491
- public appId: string;
492
- public userId: string;
521
+ getSdkVersion(callback: (version: string) => void) {
522
+ callback(VERSION);
523
+ }
493
524
 
494
- constructor(config: {appId: string; userId: string}) {
495
- this.appId = config.appId;
496
- this.userId = config.userId;
525
+ addListener<EventType extends keyof RtmClientEvents>(
526
+ event: EventType,
527
+ listener: RtmClientEvents[EventType],
528
+ ): Subscription {
529
+ if (event === 'ChannelAttributesUpdated') {
530
+ this.channelEventsMap.set(event, listener as callbackType);
531
+ }
532
+ return {
533
+ remove: () => {
534
+ console.log(
535
+ 'Use destroy method to remove all the event listeners from the RtcEngine instead.',
536
+ );
537
+ },
538
+ };
497
539
  }
498
540
  }
499
- // Factory function to create RTM client
500
- export function createAgoraRtmClient(config: RtmConfig): RTMWebClient {
501
- return new RTMWebClient(config.appId, config.userId);
502
- }