agora-appbuilder-core 4.1.22-beta.5 → 4.1.22-beta.8

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.1.22-beta.5",
3
+ "version": "4.1.22-beta.8",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -19,6 +19,7 @@
19
19
  "@splinetool/runtime": "^1.9.69",
20
20
  "@supersami/rn-foreground-service": "^1.1.1",
21
21
  "add": "^2.0.6",
22
+ "agora-agent-client-toolkit": "2.9.0",
22
23
  "agora-chat": "1.2.2-3",
23
24
  "agora-extension-ai-denoiser": "1.1.0",
24
25
  "agora-extension-beauty-effect": "^1.0.2-beta",
@@ -7977,6 +7978,28 @@
7977
7978
  "integrity": "sha512-Io9f2PpBG3XSKa/tk4krz29UibbEqqpf6crdJNpIS95PTVPP8sLw0lffgKHX9F3TKjzMpN2/uVgkaGVqZ2Y3XA==",
7978
7979
  "license": "ISC"
7979
7980
  },
7981
+ "node_modules/agora-agent-client-toolkit": {
7982
+ "version": "2.9.0",
7983
+ "resolved": "https://registry.npmjs.org/agora-agent-client-toolkit/-/agora-agent-client-toolkit-2.9.0.tgz",
7984
+ "integrity": "sha512-Ep5RsXN1zcH0pkjJxFzY+4yWTmtmzUZmW6Iro+psbxWqKMirTffFjrlRY7UN/G8+gLQR+xCNG+sTcgDJfQvldg==",
7985
+ "license": "MIT",
7986
+ "engines": {
7987
+ "node": ">=20.0.0"
7988
+ },
7989
+ "optionalDependencies": {
7990
+ "@agora-js/report": ">=4.19.0",
7991
+ "jszip": ">=3.0.0"
7992
+ },
7993
+ "peerDependencies": {
7994
+ "agora-rtc-sdk-ng": ">=4.23.4",
7995
+ "agora-rtm": ">=2.0.0"
7996
+ },
7997
+ "peerDependenciesMeta": {
7998
+ "agora-rtm": {
7999
+ "optional": true
8000
+ }
8001
+ }
8002
+ },
7980
8003
  "node_modules/agora-extension-ai-denoiser": {
7981
8004
  "version": "1.1.0",
7982
8005
  "resolved": "https://registry.npmjs.org/agora-extension-ai-denoiser/-/agora-extension-ai-denoiser-1.1.0.tgz",
@@ -38663,6 +38686,15 @@
38663
38686
  "resolved": "https://registry.npmjs.org/agora-chat/-/agora-chat-1.2.2-3.tgz",
38664
38687
  "integrity": "sha512-Io9f2PpBG3XSKa/tk4krz29UibbEqqpf6crdJNpIS95PTVPP8sLw0lffgKHX9F3TKjzMpN2/uVgkaGVqZ2Y3XA=="
38665
38688
  },
38689
+ "agora-agent-client-toolkit": {
38690
+ "version": "2.9.0",
38691
+ "resolved": "https://registry.npmjs.org/agora-agent-client-toolkit/-/agora-agent-client-toolkit-2.9.0.tgz",
38692
+ "integrity": "sha512-Ep5RsXN1zcH0pkjJxFzY+4yWTmtmzUZmW6Iro+psbxWqKMirTffFjrlRY7UN/G8+gLQR+xCNG+sTcgDJfQvldg==",
38693
+ "requires": {
38694
+ "@agora-js/report": ">=4.19.0",
38695
+ "jszip": ">=3.0.0"
38696
+ }
38697
+ },
38666
38698
  "agora-extension-ai-denoiser": {
38667
38699
  "version": "1.1.0",
38668
38700
  "resolved": "https://registry.npmjs.org/agora-extension-ai-denoiser/-/agora-extension-ai-denoiser-1.1.0.tgz",
@@ -3,6 +3,7 @@ import {beforeEach, describe, expect, it, jest} from '@jest/globals';
3
3
  const mockSetChannelMetadata = jest.fn<any>();
4
4
  const mockGetChannelMetadata = jest.fn<any>();
5
5
  const mockRemoveChannelMetadata = jest.fn<any>();
6
+ const mockRemoveUserMetadata = jest.fn<any>();
6
7
 
7
8
  jest.mock('agora-rtm-sdk', () => ({
8
9
  __esModule: true,
@@ -15,6 +16,7 @@ jest.mock('agora-rtm-sdk', () => ({
15
16
  setChannelMetadata: mockSetChannelMetadata,
16
17
  getChannelMetadata: mockGetChannelMetadata,
17
18
  removeChannelMetadata: mockRemoveChannelMetadata,
19
+ removeUserMetadata: mockRemoveUserMetadata,
18
20
  },
19
21
  presence: {},
20
22
  })),
@@ -30,9 +32,22 @@ describe('RTMWebClient channel metadata bridge', () => {
30
32
  jest.clearAllMocks();
31
33
  mockSetChannelMetadata.mockResolvedValue({});
32
34
  mockRemoveChannelMetadata.mockResolvedValue({});
35
+ mockRemoveUserMetadata.mockResolvedValue({});
33
36
  client = new RTMWebClient('app-id', 'user-id');
34
37
  });
35
38
 
39
+ it('clears all metadata for the requested user when data is omitted', async () => {
40
+ await client.storage.removeUserMetadata({userId: 'agent-user'});
41
+
42
+ expect(mockRemoveUserMetadata).toHaveBeenCalledWith({
43
+ userId: 'agent-user',
44
+ majorRevision: undefined,
45
+ lockName: undefined,
46
+ addUserId: true,
47
+ addTimeStamp: true,
48
+ });
49
+ });
50
+
36
51
  it('preserves revision zero for create-only channel metadata', async () => {
37
52
  await client.storage.setChannelMetadata(
38
53
  'room',
@@ -2,6 +2,7 @@ import {
2
2
  type Metadata as NativeMetadata,
3
3
  type MetadataItem as NativeMetadataItem,
4
4
  type GetUserMetadataOptions as NativeGetUserMetadataOptions,
5
+ type RemoveUserMetadataOptions as NativeRemoveUserMetadataOptions,
5
6
  type RtmChannelType as NativeRtmChannelType,
6
7
  type SetUserMetadataResponse,
7
8
  type LoginOptions as NativeLoginOptions,
@@ -72,24 +73,12 @@ export class RTMWebClient {
72
73
  private client: RTMClient;
73
74
  private appId: string;
74
75
  private userId: string;
75
- private eventsMap = new Map<keyof NativeRTMClientEventMap, Set<CallbackType>>(
76
- [
77
- ['linkState', new Set()],
78
- ['storage', new Set()],
79
- ['presence', new Set()],
80
- ['message', new Set()],
81
- ],
82
- );
83
-
84
- private emitEvent(event: keyof NativeRTMClientEventMap, data: any) {
85
- const listeners = this.eventsMap.get(event);
86
-
87
- if (!listeners) {
88
- return;
89
- }
90
-
91
- listeners.forEach(listener => listener(data));
92
- }
76
+ private eventsMap = new Map<keyof NativeRTMClientEventMap, CallbackType>([
77
+ ['linkState', () => null],
78
+ ['storage', () => null],
79
+ ['presence', () => null],
80
+ ['message', () => null],
81
+ ]);
93
82
 
94
83
  constructor(appId: string, userId: string) {
95
84
  this.appId = appId;
@@ -109,7 +98,7 @@ export class RTMWebClient {
109
98
  nativeLinkStateMapping.IDLE,
110
99
  reasonCode: linkStatusReasonCodeMapping[data.reasonCode] || 0,
111
100
  };
112
- this.emitEvent('linkState', nativeState);
101
+ (this.eventsMap.get('linkState') ?? (() => {}))(nativeState);
113
102
  });
114
103
 
115
104
  this.client.addEventListener('storage', data => {
@@ -120,7 +109,7 @@ export class RTMWebClient {
120
109
  data: convertWebToNativeMetadata(data.data),
121
110
  timestamp: data.timestamp,
122
111
  };
123
- this.emitEvent('storage', nativeStorageEvent);
112
+ (this.eventsMap.get('storage') ?? (() => {}))(nativeStorageEvent);
124
113
  });
125
114
 
126
115
  this.client.addEventListener('presence', data => {
@@ -131,7 +120,7 @@ export class RTMWebClient {
131
120
  publisher: data.publisher,
132
121
  timestamp: data.timestamp,
133
122
  };
134
- this.emitEvent('presence', nativePresenceEvent);
123
+ (this.eventsMap.get('presence') ?? (() => {}))(nativePresenceEvent);
135
124
  });
136
125
 
137
126
  this.client.addEventListener('message', data => {
@@ -141,7 +130,7 @@ export class RTMWebClient {
141
130
  messageType: nativeMessageEventTypeMapping[data.messageType],
142
131
  message: `${data.message}`,
143
132
  };
144
- this.emitEvent('message', nativeMessageEvent);
133
+ (this.eventsMap.get('message') ?? (() => {}))(nativeMessageEvent);
145
134
  });
146
135
  } catch (error) {
147
136
  const contextError = new Error(
@@ -241,6 +230,23 @@ export class RTMWebClient {
241
230
  return nativeResponse;
242
231
  },
243
232
 
233
+ removeUserMetadata: (options?: NativeRemoveUserMetadataOptions) => {
234
+ const data = options?.data?.items?.map(item => ({
235
+ key: item.key,
236
+ value: item.value ?? '',
237
+ revision: item.revision ?? -1,
238
+ }));
239
+
240
+ return this.client.storage.removeUserMetadata({
241
+ userId: options?.userId,
242
+ majorRevision: options?.majorRevision,
243
+ lockName: options?.lockName,
244
+ ...(data ? {data} : {}),
245
+ addTimeStamp: options?.addTimeStamp ?? true,
246
+ addUserId: options?.addUserId ?? true,
247
+ });
248
+ },
249
+
244
250
  setChannelMetadata: async (
245
251
  channelName: string,
246
252
  channelType: NativeRtmChannelType,
@@ -411,18 +417,21 @@ export class RTMWebClient {
411
417
  listener: (event: any) => void,
412
418
  ) {
413
419
  if (this.client) {
414
- // Web SDK listeners are fixed in the constructor; keep app-level
415
- // subscribers multiplexed here.
416
- this.eventsMap.get(event)?.add(listener as CallbackType);
420
+ // Simply replace the handler in our map - web client listeners are fixed in constructor
421
+ this.eventsMap.set(event, listener as CallbackType);
417
422
  }
418
423
  }
419
424
 
420
425
  removeEventListener(
421
426
  event: keyof NativeRTMClientEventMap,
422
- listener: (event: any) => void,
427
+ _listener: (event: any) => void,
423
428
  ) {
424
429
  if (this.client && this.eventsMap.has(event)) {
425
- this.eventsMap.get(event)?.delete(listener as CallbackType);
430
+ const prevListener = this.eventsMap.get(event);
431
+ if (prevListener) {
432
+ this.client.removeEventListener(event, prevListener);
433
+ }
434
+ this.eventsMap.set(event, () => null); // reset to no-op
426
435
  }
427
436
  }
428
437
 
@@ -485,10 +494,10 @@ export class RTMWebClient {
485
494
 
486
495
  removeAllListeners() {
487
496
  this.eventsMap = new Map([
488
- ['linkState', new Set()],
489
- ['storage', new Set()],
490
- ['presence', new Set()],
491
- ['message', new Set()],
497
+ ['linkState', () => null],
498
+ ['storage', () => null],
499
+ ['presence', () => null],
500
+ ['message', () => null],
492
501
  ]);
493
502
  return this.client.removeAllListeners();
494
503
  }
@@ -26,7 +26,6 @@ export * from './typeDefinition';
26
26
  export * from './utils';
27
27
  export * from './types';
28
28
  export * from './atoms';
29
- export * from './ai-agent';
30
29
 
31
30
  //TODO: hari remove later - used for simple-practice demo
32
31
  export * from './temp';
@@ -18,6 +18,7 @@ import {ToolbarPresetProps} from './sub-components';
18
18
  import {TextDataInterface} from '../src/language/default-labels';
19
19
  import {VBPanelProps} from '../src/components/virtual-background/VBPanel';
20
20
  import {AIAgentState} from '../src/ai-agent/components/AgentControls/const';
21
+ import type {MessageEvent, PresenceEvent} from 'agora-react-native-rtm';
21
22
 
22
23
  export const CUSTOM_ROUTES_PREFIX = '/r/';
23
24
 
@@ -136,6 +137,9 @@ export type EndCallHookType = () => (
136
137
  isHost: boolean,
137
138
  history: History,
138
139
  ) => Promise<void>;
140
+ export type DeepLinkHandlerType = (link: string) => boolean | void;
141
+ export type RtmMessageHandlerType = (message: MessageEvent) => boolean | void;
142
+ export type RtmPresenceHandlerType = (presence: PresenceEvent) => void;
139
143
 
140
144
  export interface AppConfig {
141
145
  defaultRootFallback?: React.ComponentType;
@@ -170,6 +174,9 @@ export interface CustomizationApiInterface {
170
174
  lifecycle?: {
171
175
  useAfterEndCall?: EndCallHookType;
172
176
  useBeforeEndCall?: EndCallHookType;
177
+ onDeepLink?: DeepLinkHandlerType;
178
+ onRtmMessage?: RtmMessageHandlerType;
179
+ onRtmPresence?: RtmPresenceHandlerType;
173
180
  // useBeforeJoin?: CustomHookType;
174
181
  // useBeforeCreate?: CustomHookType;
175
182
  };
@@ -77,8 +77,8 @@ const DefaultConfig = {
77
77
  CHAT_ORG_NAME: "",
78
78
  CHAT_APP_NAME: "",
79
79
  CHAT_URL: "",
80
- CLI_VERSION: "3.1.22-beta.5",
81
- CORE_VERSION: "4.1.22-beta.5",
80
+ CLI_VERSION: "3.1.22-beta.8",
81
+ CORE_VERSION: "4.1.22-beta.8",
82
82
  DISABLE_LANDSCAPE_MODE: false,
83
83
  STT_AUTO_START: false,
84
84
  CLOUD_RECORDING_AUTO_START: false,
@@ -59,6 +59,7 @@
59
59
  "@splinetool/runtime": "^1.9.69",
60
60
  "@supersami/rn-foreground-service": "^1.1.1",
61
61
  "add": "^2.0.6",
62
+ "agora-agent-client-toolkit": "2.9.0",
62
63
  "agora-chat": "1.2.2-3",
63
64
  "agora-extension-ai-denoiser": "1.1.0",
64
65
  "agora-extension-beauty-effect": "^1.0.2-beta",
@@ -43,6 +43,7 @@ import SDKEvents from '../utils/SdkEvents';
43
43
  import LocalEventEmitter, {
44
44
  LocalEventsEnum,
45
45
  } from '../rtm-events-api/LocalEvents';
46
+ import {useCustomization} from 'customization-implementation';
46
47
 
47
48
  const GET_USER_URL = `${$config.BACKEND_ENDPOINT}/v1/user/details`;
48
49
 
@@ -64,6 +65,7 @@ const AuthContext = createContext<AuthContextInterface | null>(null);
64
65
  const AuthProvider = (props: AuthProviderProps) => {
65
66
  const loadingLabel = useString(loadingText)();
66
67
  const timeoutHeading = useString(authSessionTimeoutToastHeading)();
68
+ const {lifecycle} = useCustomization();
67
69
  const regEvent = useRef(true);
68
70
  const refreshTimeoutWeb = useRef(null);
69
71
  const [showNativePopup, setShowNativePopup] = useState(false);
@@ -137,6 +139,14 @@ const AuthProvider = (props: AuthProviderProps) => {
137
139
 
138
140
  const deepLinkUrl = (link: string | null) => {
139
141
  if (link !== null) {
142
+ if (lifecycle?.onDeepLink?.(link)) {
143
+ logger.log(
144
+ LogSource.Internals,
145
+ 'AUTH',
146
+ 'Deep link handled by customization',
147
+ );
148
+ return;
149
+ }
140
150
  //deeplinking handling with authentication enabled
141
151
  if ($config.ENABLE_IDP_AUTH) {
142
152
  const url = processDeepLinkURI(link);
@@ -125,9 +125,6 @@ import {
125
125
  import ViewTextTracksModal from './text-tracks/ViewTextTracksModal';
126
126
  import LiveReactionButton from './reactions/LiveReactionButton';
127
127
 
128
- const getWhiteboardActivePayload = (status: boolean, channel?: string) =>
129
- JSON.stringify({status, channel});
130
-
131
128
  export const useToggleWhiteboard = () => {
132
129
  const {
133
130
  whiteboardActive,
@@ -138,9 +135,6 @@ export const useToggleWhiteboard = () => {
138
135
  const {setCustomContent} = useContent();
139
136
  const {setLayout} = useLayout();
140
137
  const {dispatch} = useContext(DispatchContext);
141
- const {
142
- data: {channel},
143
- } = useRoomInfo();
144
138
  return () => {
145
139
  if ($config.ENABLE_WHITEBOARD) {
146
140
  if (whiteboardActive) {
@@ -149,7 +143,7 @@ export const useToggleWhiteboard = () => {
149
143
  setLayout('grid');
150
144
  events.send(
151
145
  EventNames.WHITEBOARD_ACTIVE,
152
- getWhiteboardActivePayload(false, channel),
146
+ JSON.stringify({status: false}),
153
147
  PersistanceLevel.Session,
154
148
  );
155
149
  } else {
@@ -162,7 +156,7 @@ export const useToggleWhiteboard = () => {
162
156
  setLayout('pinned');
163
157
  events.send(
164
158
  EventNames.WHITEBOARD_ACTIVE,
165
- getWhiteboardActivePayload(true, channel),
159
+ JSON.stringify({status: true}),
166
160
  PersistanceLevel.Session,
167
161
  );
168
162
  }
@@ -175,7 +169,7 @@ export const WhiteboardListener = () => {
175
169
  const {setCustomContent} = useContent();
176
170
  const {currentLayout, setLayout} = useLayout();
177
171
  const {
178
- data: {channel, isHost},
172
+ data: {isHost},
179
173
  isWhiteBoardOn,
180
174
  } = useRoomInfo();
181
175
 
@@ -254,7 +248,7 @@ export const WhiteboardListener = () => {
254
248
  triggerEvent &&
255
249
  events.send(
256
250
  EventNames.WHITEBOARD_ACTIVE,
257
- getWhiteboardActivePayload(false, channel),
251
+ JSON.stringify({status: false}),
258
252
  PersistanceLevel.Session,
259
253
  );
260
254
  } else {
@@ -268,7 +262,7 @@ export const WhiteboardListener = () => {
268
262
  triggerEvent &&
269
263
  events.send(
270
264
  EventNames.WHITEBOARD_ACTIVE,
271
- getWhiteboardActivePayload(true, channel),
265
+ JSON.stringify({status: true}),
272
266
  PersistanceLevel.Session,
273
267
  );
274
268
  }
@@ -346,7 +340,7 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
346
340
 
347
341
  // const {start, restart} = useSTTAPI();
348
342
  const {
349
- data: {channel, isHost},
343
+ data: {isHost},
350
344
  } = useRoomInfo();
351
345
  const {setShowInvitePopup, setShowStopRecordingPopup, setShowLayoutOption} =
352
346
  useVideoCall();
@@ -477,7 +471,7 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
477
471
  triggerEvent &&
478
472
  events.send(
479
473
  EventNames.WHITEBOARD_ACTIVE,
480
- getWhiteboardActivePayload(false, channel),
474
+ JSON.stringify({status: false}),
481
475
  PersistanceLevel.Session,
482
476
  );
483
477
  } else {
@@ -491,7 +485,7 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
491
485
  triggerEvent &&
492
486
  events.send(
493
487
  EventNames.WHITEBOARD_ACTIVE,
494
- getWhiteboardActivePayload(true, channel),
488
+ JSON.stringify({status: true}),
495
489
  PersistanceLevel.Session,
496
490
  );
497
491
  }
@@ -243,12 +243,11 @@ const EventsConfigure: React.FC<Props> = ({
243
243
  defaultContentRef.current.defaultContent = defaultContent;
244
244
  }, [defaultContent]);
245
245
  const {
246
- data: {channel, isHost, roomId},
246
+ data: {isHost, roomId},
247
247
  roomPreference: {userRemovalTimeout},
248
248
  } = useRoomInfo();
249
249
  const {setRoomInfo} = useSetRoomInfo();
250
250
  const isHostRef = React.useRef(isHost);
251
- const channelRef = React.useRef(channel);
252
251
  const {permissionStatus} = useLocalUserInfo();
253
252
  const permissionStatusRef = React.useRef(permissionStatus);
254
253
  const {waitingRoomUids, waitingRoomRef} = useWaitingRoomContext();
@@ -264,13 +263,6 @@ const EventsConfigure: React.FC<Props> = ({
264
263
  useEffect(() => {
265
264
  isHostRef.current = isHost;
266
265
  }, [isHost]);
267
- useEffect(() => {
268
- channelRef.current = channel;
269
- }, [channel]);
270
- const isDifferentWhiteboardChannel = (eventChannel?: string) => {
271
- const currentChannel = channelRef.current;
272
- return !!currentChannel && eventChannel !== currentChannel;
273
- };
274
266
 
275
267
  useEffect(() => {
276
268
  permissionStatusRef.current = permissionStatus;
@@ -500,15 +492,6 @@ const EventsConfigure: React.FC<Props> = ({
500
492
 
501
493
  events.on(EventNames.WHITEBOARD_ACTIVE, ({payload}) => {
502
494
  const data = JSON.parse(payload);
503
- if (isDifferentWhiteboardChannel(data?.channel)) {
504
- logger.debug(
505
- LogSource.Internals,
506
- 'WHITEBOARD',
507
- 'Ignoring whiteboard state for a different channel',
508
- {eventChannel: data?.channel, currentChannel: channelRef.current},
509
- );
510
- return;
511
- }
512
495
  if (data && data?.status) {
513
496
  if (
514
497
  ($config.ENABLE_WAITING_ROOM && !isHostRef.current) ||
@@ -546,15 +529,6 @@ const EventsConfigure: React.FC<Props> = ({
546
529
 
547
530
  events.on(EventNames.BOARD_COLOR_CHANGED, ({payload}) => {
548
531
  const data = JSON.parse(payload);
549
- if (isDifferentWhiteboardChannel(data?.channel)) {
550
- logger.debug(
551
- LogSource.Internals,
552
- 'WHITEBOARD',
553
- 'Ignoring whiteboard board color for a different channel',
554
- {eventChannel: data?.channel, currentChannel: channelRef.current},
555
- );
556
- return;
557
- }
558
532
  if (data?.boardColor) {
559
533
  if (
560
534
  ($config.ENABLE_WAITING_ROOM && !isHostRef.current) ||
@@ -576,15 +550,6 @@ const EventsConfigure: React.FC<Props> = ({
576
550
 
577
551
  events.on(EventNames.WHITEBOARD_LAST_IMAGE_UPLOAD_POSITION, ({payload}) => {
578
552
  const data = JSON.parse(payload);
579
- if (isDifferentWhiteboardChannel(data?.channel)) {
580
- logger.debug(
581
- LogSource.Internals,
582
- 'WHITEBOARD',
583
- 'Ignoring whiteboard upload position for a different channel',
584
- {eventChannel: data?.channel, currentChannel: channelRef.current},
585
- );
586
- return;
587
- }
588
553
  if (
589
554
  ($config.ENABLE_WAITING_ROOM && !isHostRef.current) ||
590
555
  $config.AUTO_CONNECT_RTM
@@ -35,6 +35,7 @@ import {Platform} from 'react-native';
35
35
  import {backOff} from 'exponential-backoff';
36
36
  import {isAndroid, isIOS, isWeb, isWebInternal} from '../utils/common';
37
37
  import {useContent} from 'customization-api';
38
+ import {useCustomization} from 'customization-implementation';
38
39
  import {
39
40
  safeJsonParse,
40
41
  timeNow,
@@ -81,6 +82,7 @@ const RtmConfigure = (props: any) => {
81
82
  const {rtcProps} = useContext(PropsContext);
82
83
  const {dispatch} = useContext(DispatchContext);
83
84
  const {defaultContent, activeUids} = useContent();
85
+ const {lifecycle} = useCustomization();
84
86
  const {
85
87
  waitingRoomStatus,
86
88
  data: {isHost},
@@ -318,6 +320,16 @@ const RtmConfigure = (props: any) => {
318
320
  if (`${localUid}` === presence.publisher) {
319
321
  return;
320
322
  }
323
+ try {
324
+ lifecycle?.onRtmPresence?.(presence);
325
+ } catch (error) {
326
+ logger.error(
327
+ LogSource.Events,
328
+ 'CUSTOM_EVENTS',
329
+ 'Customization RTM presence handler failed',
330
+ {error, presence},
331
+ );
332
+ }
321
333
  // remoteJoinChannel
322
334
  if (presence.type === nativePresenceEventTypeMapping.REMOTE_JOIN) {
323
335
  logger.log(
@@ -325,10 +337,22 @@ const RtmConfigure = (props: any) => {
325
337
  'Event',
326
338
  'RTM presenceEvent of type [3 - remoteJoin] (channelMemberJoined)',
327
339
  );
328
- const backoffAttributes = await fetchUserAttributesWithBackoffRetry(
329
- presence.publisher,
330
- );
331
- await processUserUidAttributes(backoffAttributes, presence.publisher);
340
+ try {
341
+ const backoffAttributes = await fetchUserAttributesWithBackoffRetry(
342
+ presence.publisher,
343
+ );
344
+ await processUserUidAttributes(
345
+ backoffAttributes,
346
+ presence.publisher,
347
+ );
348
+ } catch (error) {
349
+ logger.warn(
350
+ LogSource.AgoraSDK,
351
+ 'Event',
352
+ `RTM could not apply metadata for joined member ${presence.publisher}`,
353
+ {error},
354
+ );
355
+ }
332
356
  }
333
357
  // remoteLeaveChannel and, on web, remote connection timeout
334
358
  if (isRemoteDeparture(presence.type, isWebInternal())) {
@@ -363,6 +387,20 @@ const RtmConfigure = (props: any) => {
363
387
  if (`${localUid}` === message.publisher) {
364
388
  return;
365
389
  }
390
+ let handledByCustomization = false;
391
+ try {
392
+ handledByCustomization = !!lifecycle?.onRtmMessage?.(message);
393
+ } catch (error) {
394
+ logger.error(
395
+ LogSource.Events,
396
+ 'CUSTOM_EVENTS',
397
+ 'Customization RTM message handler failed',
398
+ {error, message},
399
+ );
400
+ }
401
+ if (handledByCustomization) {
402
+ return;
403
+ }
366
404
  // message - 1 (channel)
367
405
  if (message.channelType === nativeChannelTypeMapping.MESSAGE) {
368
406
  logger.debug(
@@ -501,6 +539,7 @@ const RtmConfigure = (props: any) => {
501
539
  const options: SetOrUpdateUserMetadataOptions = {
502
540
  userId: `${localUid}`,
503
541
  };
542
+ await engine.current.storage.removeUserMetadata(options);
504
543
  await engine.current.storage.setUserMetadata(data, options);
505
544
  logger.log(
506
545
  LogSource.AgoraSDK,
@@ -816,19 +855,17 @@ const RtmConfigure = (props: any) => {
816
855
  const uid = parseInt(userId, 10);
817
856
  const screenUidItem = attr?.items?.find(item => item.key === 'screenUid');
818
857
  const isHostItem = attr?.items?.find(item => item.key === 'isHost');
819
- const nameItem = attr?.items?.find(item => item.key === 'name');
820
858
  const screenUid = screenUidItem?.value
821
859
  ? parseInt(screenUidItem.value, 10)
822
860
  : undefined;
823
861
 
824
862
  //start - updating user data in rtc
825
863
  const userData = {
826
- screenUid: screenUid,
864
+ screenUid,
827
865
  //below thing for livestreaming
828
866
  type: uid === parseInt(RECORDING_BOT_UID, 10) ? 'bot' : 'rtc',
829
867
  uid,
830
868
  offline: false,
831
- ...(nameItem?.value ? {name: nameItem.value} : {}),
832
869
  isHost: isHostItem?.value || false,
833
870
  lastMessageTimeStamp: 0,
834
871
  };
@@ -19,7 +19,7 @@ const WhiteboardButton = () => {
19
19
  leaveWhiteboardRoom,
20
20
  } = useContext(whiteboardContext);
21
21
  const {
22
- data: {channel, isHost},
22
+ data: {isHost},
23
23
  } = useRoomInfo();
24
24
 
25
25
  const {setLayout, currentLayout} = useLayout();
@@ -46,7 +46,7 @@ const WhiteboardButton = () => {
46
46
  triggerEvent &&
47
47
  customEvents.send(
48
48
  EventNames.WHITEBOARD_ACTIVE,
49
- JSON.stringify({status: false, channel}),
49
+ JSON.stringify({status: false}),
50
50
  PersistanceLevel.Session,
51
51
  );
52
52
  } else {
@@ -55,7 +55,7 @@ const WhiteboardButton = () => {
55
55
  triggerEvent &&
56
56
  customEvents.send(
57
57
  EventNames.WHITEBOARD_ACTIVE,
58
- JSON.stringify({status: true, channel}),
58
+ JSON.stringify({status: true}),
59
59
  PersistanceLevel.Session,
60
60
  );
61
61
  }
@@ -140,7 +140,7 @@ const WhiteboardConfigure: React.FC<WhiteboardPropsInterface> = props => {
140
140
 
141
141
  const [name] = useUserName();
142
142
  const {
143
- data: {channel, isHost, whiteboard: {room_token, room_uuid} = {}},
143
+ data: {isHost, whiteboard: {room_token, room_uuid} = {}},
144
144
  boardColor: boardColorRemote,
145
145
  whiteboardLastImageUploadPosition: whiteboardLastImageUploadPositionRemote,
146
146
  } = useRoomInfo();
@@ -335,7 +335,7 @@ const WhiteboardConfigure: React.FC<WhiteboardPropsInterface> = props => {
335
335
  */
336
336
  events.send(
337
337
  EventNames.WHITEBOARD_LAST_IMAGE_UPLOAD_POSITION,
338
- JSON.stringify({height: height || 0, channel}),
338
+ JSON.stringify({height: height || 0}),
339
339
  PersistanceLevel.Session,
340
340
  );
341
341
  };
@@ -519,10 +519,12 @@ const ActionSheetContent = props => {
519
519
  </View>
520
520
  </ActionSheetProvider>
521
521
 
522
- <CarouselWrapper
523
- data={displayItemsOrdered?.slice(3, displayItemsOrdered?.length)}
524
- dataObject={displayItems}
525
- />
522
+ <ActionSheetProvider>
523
+ <CarouselWrapper
524
+ data={displayItemsOrdered?.slice(3, displayItemsOrdered?.length)}
525
+ dataObject={displayItems}
526
+ />
527
+ </ActionSheetProvider>
526
528
  </View>
527
529
  );
528
530
  };
@@ -20,6 +20,7 @@ import useIsHandRaised from '../../utils/useIsHandRaised';
20
20
  import {isAndroid, isIOS} from '../../utils/common';
21
21
  import {useVideoCall} from '../../components/useVideoCall';
22
22
  import {useToolbarMenu} from '../../utils/useMenu';
23
+ import {useActionSheet} from '../../utils/useActionSheet';
23
24
  import ToolbarMenuItem from '../../atoms/ToolbarMenuItem';
24
25
  import {
25
26
  livestreamingShareTooltipText,
@@ -41,9 +42,15 @@ export interface ScreenshareButtonProps {
41
42
  const ScreenshareButton = (props: ScreenshareButtonProps) => {
42
43
  const {label = null, onPress: onPressCustom = null} = useToolbarProps();
43
44
  const {isToolbarMenuItem} = useToolbarMenu();
45
+ const {
46
+ isOnActionSheet: isOnActionSheetContext,
47
+ showLabel: showActionSheetLabel,
48
+ } = useActionSheet();
44
49
  const {rtcProps} = useContext(PropsContext);
45
- const {showLabel = $config.ICON_TEXT || false, isOnActionSheet = false} =
46
- props;
50
+ const isOnActionSheet = props.isOnActionSheet || isOnActionSheetContext;
51
+ const showLabel =
52
+ props.showLabel ??
53
+ (isOnActionSheet ? showActionSheetLabel : $config.ICON_TEXT || false);
47
54
  const {
48
55
  data: {isHost},
49
56
  } = useRoomInfo();
@@ -1,4 +0,0 @@
1
- export {
2
- default as AIAgentAudioVisualizer,
3
- DisconnectedView as AIAgentDisconnectedView,
4
- } from '../src/ai-agent/components/AudioVisualizer';