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

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 (28) hide show
  1. package/package.json +1 -1
  2. package/template/bridge/rtm/web/index.ts +36 -24
  3. package/template/customization-api/ai-agent.ts +4 -0
  4. package/template/customization-api/atoms.ts +2 -0
  5. package/template/customization-api/customEvents.ts +4 -1
  6. package/template/customization-api/index.ts +1 -0
  7. package/template/customization-api/typeDefinition.ts +6 -1
  8. package/template/defaultConfig.js +2 -2
  9. package/template/src/components/Controls.tsx +14 -8
  10. package/template/src/components/CustomSidePanel.tsx +13 -13
  11. package/template/src/components/EventsConfigure.tsx +36 -1
  12. package/template/src/components/RTMConfigure.tsx +16 -0
  13. package/template/src/components/participants/UserActionMenuOptions.tsx +28 -1
  14. package/template/src/components/whiteboard/WhiteboardButton.tsx +3 -3
  15. package/template/src/components/whiteboard/WhiteboardConfigure.tsx +2 -2
  16. package/template/src/pages/Create.tsx +31 -19
  17. package/template/src/pages/video-call/ActionSheet.native.tsx +2 -0
  18. package/template/src/pages/video-call/ActionSheet.tsx +2 -0
  19. package/template/src/pages/video-call/ActionSheetHandle.tsx +1 -0
  20. package/template/src/pages/video-call/SidePanelHeader.tsx +3 -1
  21. package/template/src/pages/video-call/VideoCallScreen.tsx +3 -0
  22. package/template/src/pages/video-call/VideoRenderer.tsx +4 -0
  23. package/template/src/subComponents/SidePanelHeader.tsx +16 -2
  24. package/template/.eslintrc.js +0 -20
  25. package/template/.prettierrc.js +0 -18
  26. package/template/.watchmanconfig +0 -1
  27. package/template/config.json +0 -101
  28. package/template/theme.json +0 -91
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.1.22-beta.3",
3
+ "version": "4.1.22-beta.5",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -72,12 +72,24 @@ export class RTMWebClient {
72
72
  private client: RTMClient;
73
73
  private appId: string;
74
74
  private userId: string;
75
- private eventsMap = new Map<keyof NativeRTMClientEventMap, CallbackType>([
76
- ['linkState', () => null],
77
- ['storage', () => null],
78
- ['presence', () => null],
79
- ['message', () => null],
80
- ]);
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
+ }
81
93
 
82
94
  constructor(appId: string, userId: string) {
83
95
  this.appId = appId;
@@ -97,7 +109,7 @@ export class RTMWebClient {
97
109
  nativeLinkStateMapping.IDLE,
98
110
  reasonCode: linkStatusReasonCodeMapping[data.reasonCode] || 0,
99
111
  };
100
- (this.eventsMap.get('linkState') ?? (() => {}))(nativeState);
112
+ this.emitEvent('linkState', nativeState);
101
113
  });
102
114
 
103
115
  this.client.addEventListener('storage', data => {
@@ -108,7 +120,7 @@ export class RTMWebClient {
108
120
  data: convertWebToNativeMetadata(data.data),
109
121
  timestamp: data.timestamp,
110
122
  };
111
- (this.eventsMap.get('storage') ?? (() => {}))(nativeStorageEvent);
123
+ this.emitEvent('storage', nativeStorageEvent);
112
124
  });
113
125
 
114
126
  this.client.addEventListener('presence', data => {
@@ -119,7 +131,7 @@ export class RTMWebClient {
119
131
  publisher: data.publisher,
120
132
  timestamp: data.timestamp,
121
133
  };
122
- (this.eventsMap.get('presence') ?? (() => {}))(nativePresenceEvent);
134
+ this.emitEvent('presence', nativePresenceEvent);
123
135
  });
124
136
 
125
137
  this.client.addEventListener('message', data => {
@@ -129,7 +141,7 @@ export class RTMWebClient {
129
141
  messageType: nativeMessageEventTypeMapping[data.messageType],
130
142
  message: `${data.message}`,
131
143
  };
132
- (this.eventsMap.get('message') ?? (() => {}))(nativeMessageEvent);
144
+ this.emitEvent('message', nativeMessageEvent);
133
145
  });
134
146
  } catch (error) {
135
147
  const contextError = new Error(
@@ -177,8 +189,11 @@ export class RTMWebClient {
177
189
  });
178
190
  // Map native signature to web signature
179
191
  return this.client.storage.setUserMetadata(validatedItems, {
180
- addTimeStamp: options?.addTimeStamp || true,
181
- addUserId: options?.addUserId || true,
192
+ userId: options?.userId,
193
+ majorRevision: options?.majorRevision,
194
+ lockName: options?.lockName,
195
+ addTimeStamp: options?.addTimeStamp ?? true,
196
+ addUserId: options?.addUserId ?? true,
182
197
  });
183
198
  },
184
199
 
@@ -396,21 +411,18 @@ export class RTMWebClient {
396
411
  listener: (event: any) => void,
397
412
  ) {
398
413
  if (this.client) {
399
- // Simply replace the handler in our map - web client listeners are fixed in constructor
400
- this.eventsMap.set(event, listener as CallbackType);
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);
401
417
  }
402
418
  }
403
419
 
404
420
  removeEventListener(
405
421
  event: keyof NativeRTMClientEventMap,
406
- _listener: (event: any) => void,
422
+ listener: (event: any) => void,
407
423
  ) {
408
424
  if (this.client && this.eventsMap.has(event)) {
409
- const prevListener = this.eventsMap.get(event);
410
- if (prevListener) {
411
- this.client.removeEventListener(event, prevListener);
412
- }
413
- this.eventsMap.set(event, () => null); // reset to no-op
425
+ this.eventsMap.get(event)?.delete(listener as CallbackType);
414
426
  }
415
427
  }
416
428
 
@@ -473,10 +485,10 @@ export class RTMWebClient {
473
485
 
474
486
  removeAllListeners() {
475
487
  this.eventsMap = new Map([
476
- ['linkState', () => null],
477
- ['storage', () => null],
478
- ['presence', () => null],
479
- ['message', () => null],
488
+ ['linkState', new Set()],
489
+ ['storage', new Set()],
490
+ ['presence', new Set()],
491
+ ['message', new Set()],
480
492
  ]);
481
493
  return this.client.removeAllListeners();
482
494
  }
@@ -0,0 +1,4 @@
1
+ export {
2
+ default as AIAgentAudioVisualizer,
3
+ DisconnectedView as AIAgentDisconnectedView,
4
+ } from '../src/ai-agent/components/AudioVisualizer';
@@ -8,3 +8,5 @@ export {default as TertiaryButton} from '../src/atoms/TertiaryButton';
8
8
  export {default as ActionMenu} from '../src/atoms/ActionMenu';
9
9
  export {default as IconButton} from '../src/atoms/IconButton';
10
10
  export {default as Dropdown} from '../src/atoms/Dropdown';
11
+ export {default as Popup} from '../src/atoms/Popup';
12
+ export {default as Tooltip} from '../src/atoms/Tooltip';
@@ -5,10 +5,13 @@ import {
5
5
  PersistanceLevel,
6
6
  EventCallback,
7
7
  } from '../src/rtm-events-api';
8
+ import LocalEventEmitter, {
9
+ LocalEventsEnum,
10
+ } from '../src/rtm-events-api/LocalEvents';
8
11
 
9
12
  // 2. Initialize with source "fpe"
10
13
  const customEvents = new Events(EventSource.fpe);
11
14
 
12
15
  // 3. export
13
- export {customEvents, PersistanceLevel};
16
+ export {customEvents, PersistanceLevel, LocalEventEmitter, LocalEventsEnum};
14
17
  export type {EventCallback};
@@ -26,6 +26,7 @@ export * from './typeDefinition';
26
26
  export * from './utils';
27
27
  export * from './types';
28
28
  export * from './atoms';
29
+ export * from './ai-agent';
29
30
 
30
31
  //TODO: hari remove later - used for simple-practice demo
31
32
  export * from './temp';
@@ -53,6 +53,7 @@ export interface SidePanelItem {
53
53
  name: string;
54
54
  title: string;
55
55
  component: React.ComponentType;
56
+ headerRightSlot?: React.ReactNode;
56
57
  onClose?: () => void;
57
58
  }
58
59
 
@@ -74,6 +75,10 @@ export type CustomLogger = (
74
75
  export interface CustomAgentInterfaceProps {
75
76
  connectionState: AIAgentState;
76
77
  }
78
+ export interface CreateInterface extends BeforeAndAfterInterface {
79
+ headerRightSlot?: React.ComponentType;
80
+ }
81
+
77
82
  export interface VideoCallInterface extends BeforeAndAfterInterface {
78
83
  // commented for v1 release
79
84
  topToolBar?: ToolbarType;
@@ -109,7 +114,7 @@ export type ComponentsInterface = {
109
114
  precall?: PreCallInterface;
110
115
  preferenceWrapper?: React.ComponentType;
111
116
  //precall?: React.ComponentType;
112
- create?: React.ComponentType;
117
+ create?: React.ComponentType | CreateInterface;
113
118
  //share?: React.ComponentType;
114
119
  //join?: React.ComponentType;
115
120
  videoCall?: VideoCallInterface;
@@ -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.3",
81
- CORE_VERSION: "4.1.22-beta.3",
80
+ CLI_VERSION: "3.1.22-beta.5",
81
+ CORE_VERSION: "4.1.22-beta.5",
82
82
  DISABLE_LANDSCAPE_MODE: false,
83
83
  STT_AUTO_START: false,
84
84
  CLOUD_RECORDING_AUTO_START: false,
@@ -125,6 +125,9 @@ 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
+
128
131
  export const useToggleWhiteboard = () => {
129
132
  const {
130
133
  whiteboardActive,
@@ -135,6 +138,9 @@ export const useToggleWhiteboard = () => {
135
138
  const {setCustomContent} = useContent();
136
139
  const {setLayout} = useLayout();
137
140
  const {dispatch} = useContext(DispatchContext);
141
+ const {
142
+ data: {channel},
143
+ } = useRoomInfo();
138
144
  return () => {
139
145
  if ($config.ENABLE_WHITEBOARD) {
140
146
  if (whiteboardActive) {
@@ -143,7 +149,7 @@ export const useToggleWhiteboard = () => {
143
149
  setLayout('grid');
144
150
  events.send(
145
151
  EventNames.WHITEBOARD_ACTIVE,
146
- JSON.stringify({status: false}),
152
+ getWhiteboardActivePayload(false, channel),
147
153
  PersistanceLevel.Session,
148
154
  );
149
155
  } else {
@@ -156,7 +162,7 @@ export const useToggleWhiteboard = () => {
156
162
  setLayout('pinned');
157
163
  events.send(
158
164
  EventNames.WHITEBOARD_ACTIVE,
159
- JSON.stringify({status: true}),
165
+ getWhiteboardActivePayload(true, channel),
160
166
  PersistanceLevel.Session,
161
167
  );
162
168
  }
@@ -169,7 +175,7 @@ export const WhiteboardListener = () => {
169
175
  const {setCustomContent} = useContent();
170
176
  const {currentLayout, setLayout} = useLayout();
171
177
  const {
172
- data: {isHost},
178
+ data: {channel, isHost},
173
179
  isWhiteBoardOn,
174
180
  } = useRoomInfo();
175
181
 
@@ -248,7 +254,7 @@ export const WhiteboardListener = () => {
248
254
  triggerEvent &&
249
255
  events.send(
250
256
  EventNames.WHITEBOARD_ACTIVE,
251
- JSON.stringify({status: false}),
257
+ getWhiteboardActivePayload(false, channel),
252
258
  PersistanceLevel.Session,
253
259
  );
254
260
  } else {
@@ -262,7 +268,7 @@ export const WhiteboardListener = () => {
262
268
  triggerEvent &&
263
269
  events.send(
264
270
  EventNames.WHITEBOARD_ACTIVE,
265
- JSON.stringify({status: true}),
271
+ getWhiteboardActivePayload(true, channel),
266
272
  PersistanceLevel.Session,
267
273
  );
268
274
  }
@@ -340,7 +346,7 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
340
346
 
341
347
  // const {start, restart} = useSTTAPI();
342
348
  const {
343
- data: {isHost},
349
+ data: {channel, isHost},
344
350
  } = useRoomInfo();
345
351
  const {setShowInvitePopup, setShowStopRecordingPopup, setShowLayoutOption} =
346
352
  useVideoCall();
@@ -471,7 +477,7 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
471
477
  triggerEvent &&
472
478
  events.send(
473
479
  EventNames.WHITEBOARD_ACTIVE,
474
- JSON.stringify({status: false}),
480
+ getWhiteboardActivePayload(false, channel),
475
481
  PersistanceLevel.Session,
476
482
  );
477
483
  } else {
@@ -485,7 +491,7 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
485
491
  triggerEvent &&
486
492
  events.send(
487
493
  EventNames.WHITEBOARD_ACTIVE,
488
- JSON.stringify({status: true}),
494
+ getWhiteboardActivePayload(true, channel),
489
495
  PersistanceLevel.Session,
490
496
  );
491
497
  }
@@ -10,23 +10,18 @@
10
10
  *********************************************
11
11
  */
12
12
  import React from 'react';
13
- import {View, Text, StyleSheet, ScrollView} from 'react-native';
13
+ import {View, StyleSheet, ScrollView} from 'react-native';
14
14
  import {isMobileUA, isWebInternal, useIsSmall} from '../utils/common';
15
15
  import CommonStyles from './CommonStyles';
16
- import SidePanelHeader, {
17
- SidePanelStyles,
18
- } from '../subComponents/SidePanelHeader';
19
- import {useLayout} from '../utils/useLayout';
20
- import {getGridLayoutName} from '../pages/video-call/DefaultLayouts';
21
16
  import useCaptionWidth from '../subComponents/caption/useCaptionWidth';
22
- import {useSidePanel} from '../utils/useSidePanel';
23
- import {SidePanelType} from '../subComponents/SidePanelEnum';
24
17
  import {CustomSidePanelHeader} from '../pages/video-call/SidePanelHeader';
25
-
18
+ import {useLayout} from '../utils/useLayout';
19
+ import {getGridLayoutName} from '../pages/video-call/DefaultLayouts';
26
20
  export interface CustomSidePanelViewInterface {
27
21
  name: string;
28
22
  title?: string;
29
23
  content: React.ComponentType;
24
+ headerRightSlot?: React.ReactNode;
30
25
  onClose?: () => void;
31
26
  showHeader?: boolean;
32
27
  }
@@ -37,12 +32,12 @@ const CustomSidePanelView = (props: CustomSidePanelViewInterface) => {
37
32
  showHeader = true,
38
33
  name,
39
34
  title,
35
+ headerRightSlot,
40
36
  onClose,
41
37
  } = props;
42
- const {currentLayout} = useLayout();
43
38
  const {transcriptHeight} = useCaptionWidth();
44
- const {setSidePanel} = useSidePanel();
45
39
  const isSmall = useIsSmall();
40
+ const {currentLayout} = useLayout();
46
41
 
47
42
  return (
48
43
  <View
@@ -57,13 +52,18 @@ const CustomSidePanelView = (props: CustomSidePanelViewInterface) => {
57
52
  : // desktop maximized
58
53
  CommonStyles.sidePanelContainerWeb,
59
54
  isWebInternal() && !isSmall() && currentLayout === getGridLayoutName()
60
- ? {marginVertical: 4}
55
+ ? {marginTop: 4}
61
56
  : {},
62
57
  //@ts-ignore
63
58
  transcriptHeight && !isMobileUA() && {height: transcriptHeight},
64
59
  ]}>
65
60
  {showHeader && (
66
- <CustomSidePanelHeader name={name} title={title} onClose={onClose} />
61
+ <CustomSidePanelHeader
62
+ name={name}
63
+ title={title}
64
+ headerRightSlot={headerRightSlot}
65
+ onClose={onClose}
66
+ />
67
67
  )}
68
68
  <ScrollView contentContainerStyle={[style.bodyContainer]}>
69
69
  {CustomSidePanelContent ? <CustomSidePanelContent /> : <></>}
@@ -243,11 +243,12 @@ const EventsConfigure: React.FC<Props> = ({
243
243
  defaultContentRef.current.defaultContent = defaultContent;
244
244
  }, [defaultContent]);
245
245
  const {
246
- data: {isHost, roomId},
246
+ data: {channel, 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);
251
252
  const {permissionStatus} = useLocalUserInfo();
252
253
  const permissionStatusRef = React.useRef(permissionStatus);
253
254
  const {waitingRoomUids, waitingRoomRef} = useWaitingRoomContext();
@@ -263,6 +264,13 @@ const EventsConfigure: React.FC<Props> = ({
263
264
  useEffect(() => {
264
265
  isHostRef.current = isHost;
265
266
  }, [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
+ };
266
274
 
267
275
  useEffect(() => {
268
276
  permissionStatusRef.current = permissionStatus;
@@ -492,6 +500,15 @@ const EventsConfigure: React.FC<Props> = ({
492
500
 
493
501
  events.on(EventNames.WHITEBOARD_ACTIVE, ({payload}) => {
494
502
  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
+ }
495
512
  if (data && data?.status) {
496
513
  if (
497
514
  ($config.ENABLE_WAITING_ROOM && !isHostRef.current) ||
@@ -529,6 +546,15 @@ const EventsConfigure: React.FC<Props> = ({
529
546
 
530
547
  events.on(EventNames.BOARD_COLOR_CHANGED, ({payload}) => {
531
548
  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
+ }
532
558
  if (data?.boardColor) {
533
559
  if (
534
560
  ($config.ENABLE_WAITING_ROOM && !isHostRef.current) ||
@@ -550,6 +576,15 @@ const EventsConfigure: React.FC<Props> = ({
550
576
 
551
577
  events.on(EventNames.WHITEBOARD_LAST_IMAGE_UPLOAD_POSITION, ({payload}) => {
552
578
  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
+ }
553
588
  if (
554
589
  ($config.ENABLE_WAITING_ROOM && !isHostRef.current) ||
555
590
  $config.AUTO_CONNECT_RTM
@@ -816,6 +816,7 @@ const RtmConfigure = (props: any) => {
816
816
  const uid = parseInt(userId, 10);
817
817
  const screenUidItem = attr?.items?.find(item => item.key === 'screenUid');
818
818
  const isHostItem = attr?.items?.find(item => item.key === 'isHost');
819
+ const nameItem = attr?.items?.find(item => item.key === 'name');
819
820
  const screenUid = screenUidItem?.value
820
821
  ? parseInt(screenUidItem.value, 10)
821
822
  : undefined;
@@ -827,6 +828,7 @@ const RtmConfigure = (props: any) => {
827
828
  type: uid === parseInt(RECORDING_BOT_UID, 10) ? 'bot' : 'rtc',
828
829
  uid,
829
830
  offline: false,
831
+ ...(nameItem?.value ? {name: nameItem.value} : {}),
830
832
  isHost: isHostItem?.value || false,
831
833
  lastMessageTimeStamp: 0,
832
834
  };
@@ -950,6 +952,20 @@ const RtmConfigure = (props: any) => {
950
952
  }
951
953
  }
952
954
 
955
+ if (
956
+ typeof evt !== 'string' ||
957
+ evt.trim() === '' ||
958
+ typeof value === 'undefined'
959
+ ) {
960
+ logger.debug(
961
+ LogSource.Events,
962
+ 'CUSTOM_EVENTS',
963
+ 'Ignoring RTM payload: not an AppBuilder custom-event envelope',
964
+ {data, sender, ts},
965
+ );
966
+ return;
967
+ }
968
+
953
969
  try {
954
970
  let parsedValue;
955
971
  try {
@@ -87,6 +87,7 @@ interface UserActionMenuOptionsOptionsProps {
87
87
  spotlightUid?: UidType;
88
88
  setSpotlightUid?: (uid: UidType) => void;
89
89
  items?: UserActionMenuItemsConfig;
90
+ extraMenuItems?: ActionMenuItem[];
90
91
  }
91
92
 
92
93
  export default function UserActionMenuOptionsOptions(
@@ -102,7 +103,13 @@ export default function UserActionMenuOptionsOptions(
102
103
  useState(false);
103
104
  const [actionMenuitems, setActionMenuitems] = useState<ActionMenuItem[]>([]);
104
105
  const {setSidePanel} = useSidePanel();
105
- const {user, actionMenuVisible, setActionMenuVisible, spotlightUid} = props;
106
+ const {
107
+ user,
108
+ actionMenuVisible,
109
+ setActionMenuVisible,
110
+ spotlightUid,
111
+ extraMenuItems,
112
+ } = props;
106
113
  const {currentLayout} = useLayout();
107
114
  const {pinnedUid, activeUids, customContent, secondaryPinnedUid} =
108
115
  useContent();
@@ -716,6 +723,24 @@ export default function UserActionMenuOptionsOptions(
716
723
  items.push(...customItems);
717
724
  }
718
725
 
726
+ if (extraMenuItems?.length) {
727
+ items.push(
728
+ ...extraMenuItems.map(item => ({
729
+ ...item,
730
+ closeActionMenu: () => {
731
+ item.closeActionMenu?.();
732
+ setActionMenuVisible(false);
733
+ },
734
+ onPress: item.onPress
735
+ ? () => {
736
+ item.onPress?.();
737
+ setActionMenuVisible(false);
738
+ }
739
+ : item.onPress,
740
+ })),
741
+ );
742
+ }
743
+
719
744
  items.sort((a, b) => (a.order ?? 999) - (b.order ?? 999));
720
745
 
721
746
  setActionMenuitems(items);
@@ -729,6 +754,8 @@ export default function UserActionMenuOptionsOptions(
729
754
  secondaryPinnedUid,
730
755
  currentLayout,
731
756
  spotlightUid,
757
+ extraMenuItems,
758
+ setActionMenuVisible,
732
759
  ]);
733
760
 
734
761
  const {width: globalWidth, height: globalHeight} = useWindowDimensions();
@@ -19,7 +19,7 @@ const WhiteboardButton = () => {
19
19
  leaveWhiteboardRoom,
20
20
  } = useContext(whiteboardContext);
21
21
  const {
22
- data: {isHost},
22
+ data: {channel, 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}),
49
+ JSON.stringify({status: false, channel}),
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}),
58
+ JSON.stringify({status: true, channel}),
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: {isHost, whiteboard: {room_token, room_uuid} = {}},
143
+ data: {channel, 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}),
338
+ JSON.stringify({height: height || 0, channel}),
339
339
  PersistanceLevel.Session,
340
340
  );
341
341
  };
@@ -29,7 +29,6 @@ import {useCustomization} from 'customization-implementation';
29
29
  import {useString} from '../utils/useString';
30
30
  import useCreateRoom from '../utils/useCreateRoom';
31
31
  import {CreateProvider} from './create/useCreate';
32
- import useJoinRoom from '../utils/useJoinRoom';
33
32
  import {
34
33
  RoomInfoDefaultValue,
35
34
  useRoomInfo,
@@ -39,12 +38,10 @@ import Toggle from '../atoms/Toggle';
39
38
  import Card from '../atoms/Card';
40
39
  import Spacer from '../atoms/Spacer';
41
40
  import LinkButton from '../atoms/LinkButton';
42
- import StorageContext from '../components/StorageContext';
43
41
  import ThemeConfig from '../theme';
44
42
  import Tooltip from '../atoms/Tooltip';
45
43
  import ImageIcon from '../atoms/ImageIcon';
46
44
  import hexadecimalTransparency from '../utils/hexadecimalTransparency';
47
- import {randomNameGenerator} from '../utils';
48
45
  import {useSetRoomInfo} from '../components/room-info/useSetRoomInfo';
49
46
  import IDPLogoutComponent from '../auth/IDPLogoutComponent';
50
47
  import isSDK from '../utils/isSDK';
@@ -67,25 +64,35 @@ import {LogSource, logger} from '../logger/AppBuilderLogger';
67
64
  import SDKEvents from '../utils/SdkEvents';
68
65
 
69
66
  const Create = () => {
70
- const {CreateComponent} = useCustomization(data => {
71
- let components: {
72
- CreateComponent?: React.ElementType;
73
- } = {};
74
- if (
75
- data?.components?.create &&
76
- typeof data?.components?.create !== 'object'
77
- ) {
67
+ const {CreateComponent, CreateHeaderRightSlotComponent} = useCustomization(
68
+ data => {
69
+ let components: {
70
+ CreateComponent?: React.ElementType;
71
+ CreateHeaderRightSlotComponent?: React.ElementType;
72
+ } = {};
78
73
  if (
79
74
  data?.components?.create &&
80
- isValidReactComponent(data?.components?.create)
81
- )
82
- components.CreateComponent = data?.components?.create;
83
- }
84
- return components;
85
- });
75
+ typeof data?.components?.create !== 'object'
76
+ ) {
77
+ if (
78
+ data?.components?.create &&
79
+ isValidReactComponent(data?.components?.create)
80
+ )
81
+ components.CreateComponent = data?.components?.create;
82
+ }
83
+ if (
84
+ data?.components?.create &&
85
+ typeof data?.components?.create === 'object' &&
86
+ data?.components?.create?.headerRightSlot &&
87
+ isValidReactComponent(data?.components?.create?.headerRightSlot)
88
+ ) {
89
+ components.CreateHeaderRightSlotComponent =
90
+ data?.components?.create?.headerRightSlot;
91
+ }
92
+ return components;
93
+ },
94
+ );
86
95
 
87
- const useJoin = useJoinRoom();
88
- const {setStore} = useContext(StorageContext);
89
96
  const {setGlobalErrorMessage} = useContext(ErrorContext);
90
97
  const history = useHistory();
91
98
  const [loading, setLoading] = useState(false);
@@ -313,6 +320,11 @@ const Create = () => {
313
320
  <View>
314
321
  <View style={style.logoContainerStyle}>
315
322
  <Logo />
323
+ {CreateHeaderRightSlotComponent ? (
324
+ <CreateHeaderRightSlotComponent />
325
+ ) : (
326
+ <></>
327
+ )}
316
328
  {isMobileUA() ? (
317
329
  <IDPLogoutComponent
318
330
  containerStyle={{marginTop: 0, marginRight: 0}}
@@ -291,6 +291,8 @@ const ActionSheet = props => {
291
291
  isCustomSidePanel={true}
292
292
  customSidePanelProps={{
293
293
  title: sidePanelArray[customSidePanelIndex]?.title,
294
+ headerRightSlot:
295
+ sidePanelArray[customSidePanelIndex]?.headerRightSlot,
294
296
  onClose: sidePanelArray[customSidePanelIndex]?.onClose,
295
297
  name: sidePanelArray[customSidePanelIndex]?.name,
296
298
  }}
@@ -326,6 +326,8 @@ const ActionSheet = props => {
326
326
  isCustomSidePanel={true}
327
327
  customSidePanelProps={{
328
328
  title: sidePanelArray[customSidePanelIndex]?.title,
329
+ headerRightSlot:
330
+ sidePanelArray[customSidePanelIndex]?.headerRightSlot,
329
331
  onClose: sidePanelArray[customSidePanelIndex]?.onClose,
330
332
  name: sidePanelArray[customSidePanelIndex]?.name,
331
333
  }}
@@ -16,6 +16,7 @@ const ActionSheetHandle = (props: {
16
16
  isCustomSidePanel?: boolean;
17
17
  customSidePanelProps?: {
18
18
  title: string;
19
+ headerRightSlot?: React.ReactNode;
19
20
  onClose?: () => void;
20
21
  name: string;
21
22
  };
@@ -208,6 +208,7 @@ export const VBHeader = () => {
208
208
 
209
209
  export const CustomSidePanelHeader = (props: {
210
210
  title: string;
211
+ headerRightSlot?: React.ReactNode;
211
212
  onClose?: () => void;
212
213
  name: string;
213
214
  }) => {
@@ -217,6 +218,7 @@ export const CustomSidePanelHeader = (props: {
217
218
  centerComponent={
218
219
  <Text style={SidePanelStyles.heading}>{props?.title}</Text>
219
220
  }
221
+ trailingComponent={props?.headerRightSlot}
220
222
  trailingIconName="close"
221
223
  trailingIconOnPress={() => {
222
224
  setSidePanel(SidePanelType.None);
@@ -224,7 +226,7 @@ export const CustomSidePanelHeader = (props: {
224
226
  props?.onClose && props?.onClose();
225
227
  } catch (error) {
226
228
  console.error(
227
- `Error on calling onClose in custom side panel ${name}`,
229
+ `Error on calling onClose in custom side panel ${props.name}`,
228
230
  );
229
231
  }
230
232
  }}
@@ -400,6 +400,9 @@ const VideoCallScreen = () => {
400
400
  <CustomSidePanelView
401
401
  content={SidePanelArray[customSidePanelIndex]?.component}
402
402
  title={SidePanelArray[customSidePanelIndex]?.title}
403
+ headerRightSlot={
404
+ SidePanelArray[customSidePanelIndex]?.headerRightSlot
405
+ }
403
406
  name={SidePanelArray[customSidePanelIndex]?.name}
404
407
  onClose={SidePanelArray[customSidePanelIndex]?.onClose}
405
408
  />
@@ -25,6 +25,7 @@ import {
25
25
  } from './DefaultLayouts';
26
26
  import IconButton from '../../atoms/IconButton';
27
27
  import UserActionMenuOptionsOptions from '../../components/participants/UserActionMenuOptions';
28
+ import type {ActionMenuItem} from '../../atoms/ActionMenu';
28
29
  import {
29
30
  isMobileUA,
30
31
  isValidReactComponent,
@@ -60,6 +61,7 @@ export interface VideoRendererProps {
60
61
  CustomChild?: React.ComponentType;
61
62
  avatarRadius?: number;
62
63
  hideMenuOptions?: boolean;
64
+ extraMenuItems?: ActionMenuItem[];
63
65
  containerStyle?: ViewStyle;
64
66
  innerContainerStyle?: ViewStyle;
65
67
  }
@@ -69,6 +71,7 @@ const VideoRenderer: React.FC<VideoRendererProps> = ({
69
71
  CustomChild,
70
72
  avatarRadius = 100,
71
73
  hideMenuOptions = false,
74
+ extraMenuItems,
72
75
  containerStyle = {},
73
76
  innerContainerStyle = {},
74
77
  }) => {
@@ -214,6 +217,7 @@ const VideoRenderer: React.FC<VideoRendererProps> = ({
214
217
  from={'video-tile'}
215
218
  setSpotlightUid={setSpotlightUid}
216
219
  spotlightUid={spotlightUid}
220
+ extraMenuItems={extraMenuItems}
217
221
  />
218
222
  <PlatformWrapper isHovered={isHovered} setIsHovered={setIsHovered}>
219
223
  <View
@@ -13,6 +13,7 @@ export interface SidePanelHeaderProps {
13
13
  leadingIconOnPress?: () => void;
14
14
  trailingIconName?: keyof IconsInterface;
15
15
  trailingIconOnPress?: () => void;
16
+ trailingComponent?: React.ReactNode;
16
17
  trailingIconName2?: keyof IconsInterface;
17
18
  trailingIconOnPress2?: () => void;
18
19
  isChat?: boolean;
@@ -56,9 +57,21 @@ const SidePanelHeader = React.forwardRef<View, SidePanelHeaderProps>(
56
57
  <View style={{width: 30, height: 'auto'}}></View>
57
58
  ) : null}
58
59
  {props?.centerComponent ? props.centerComponent : null}
59
- <View style={props?.trailingIconName2 && SidePanelStyles.row}>
60
+ <View
61
+ style={
62
+ (props?.trailingIconName2 || props?.trailingComponent) &&
63
+ SidePanelStyles.row
64
+ }>
65
+ {props?.trailingComponent ? props.trailingComponent : null}
60
66
  {props?.trailingIconName ? (
61
- <View ref={ref} collapsable={false} style={{flex: 1}}>
67
+ <View
68
+ ref={ref}
69
+ collapsable={false}
70
+ style={
71
+ props?.trailingIconName2 || props?.trailingComponent
72
+ ? {}
73
+ : {flex: 1}
74
+ }>
62
75
  <IconButton
63
76
  hoverEffect={true}
64
77
  hoverEffectStyle={{
@@ -116,6 +129,7 @@ export const SidePanelStyles = StyleSheet.create({
116
129
  borderBottomWidth: 1,
117
130
  borderBottomColor: $config.CARD_LAYER_3_COLOR,
118
131
  position: 'relative',
132
+ zIndex: 1000,
119
133
  },
120
134
  chatPadding: {
121
135
  paddingHorizontal: 16,
@@ -1,20 +0,0 @@
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
- module.exports = {
13
- root: true,
14
- extends: '@react-native',
15
- parser: '@typescript-eslint/parser',
16
- plugins: ['@typescript-eslint'],
17
- globals: {
18
- $config: true,
19
- },
20
- };
@@ -1,18 +0,0 @@
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
- module.exports = {
13
- arrowParens: 'avoid',
14
- bracketSameLine: true,
15
- bracketSpacing: false,
16
- singleQuote: true,
17
- trailingComma: 'all',
18
- };
@@ -1 +0,0 @@
1
- {}
@@ -1,101 +0,0 @@
1
- {
2
- "PROJECT_ID": "49c705c1c9efb71000d7",
3
- "APP_ID": "a569f8fb0309417780b793786b534a86",
4
- "PRODUCT_ID": "helloworld",
5
- "APP_NAME": "HelloWorld",
6
- "LOGO": "data:image/png;base64,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",
7
- "ICON": "logoSquare.png",
8
- "FRONTEND_ENDPOINT": "https://app-builder-core-git-preprod-agoraio.vercel.app",
9
- "BACKEND_ENDPOINT": "https://managedservices-preprod.rteappbuilder.com",
10
- "PSTN": true,
11
- "PRECALL": true,
12
- "CHAT": true,
13
- "CLOUD_RECORDING": true,
14
- "RECORDING_MODE": "MIX",
15
- "SCREEN_SHARING": true,
16
- "LANDING_SUB_HEADING": "The Real-Time Engagement Platform",
17
- "ENCRYPTION_ENABLED": true,
18
- "ENABLE_GOOGLE_OAUTH": false,
19
- "ENABLE_APPLE_OAUTH": false,
20
- "ENABLE_SLACK_OAUTH": false,
21
- "ENABLE_MICROSOFT_OAUTH": false,
22
- "GOOGLE_CLIENT_ID": "",
23
- "MICROSOFT_CLIENT_ID": "",
24
- "SLACK_CLIENT_ID": "",
25
- "APPLE_CLIENT_ID": "",
26
- "BUCKET_NAME": "",
27
- "BUCKET_ACCESS_KEY": "",
28
- "BUCKET_ACCESS_SECRET": "",
29
- "GOOGLE_CLIENT_SECRET": "",
30
- "MICROSOFT_CLIENT_SECRET": "",
31
- "SLACK_CLIENT_SECRET": "",
32
- "APPLE_PRIVATE_KEY": "",
33
- "APPLE_KEY_ID": "",
34
- "APPLE_TEAM_ID": "",
35
- "PSTN_EMAIL": "",
36
- "PSTN_ACCOUNT": "",
37
- "PSTN_PASSWORD": "",
38
- "RECORDING_REGION": 0,
39
- "GEO_FENCING": true,
40
- "GEO_FENCING_INCLUDE_AREA": "GLOBAL",
41
- "GEO_FENCING_EXCLUDE_AREA": "CHINA",
42
- "LOG_ENABLED": true,
43
- "EVENT_MODE": false,
44
- "RAISE_HAND": false,
45
- "AUDIO_ROOM": false,
46
- "BG": "",
47
- "PRIMARY_FONT_COLOR": "#363636",
48
- "SECONDARY_FONT_COLOR": "#FFFFFF",
49
- "PROFILE": "720p_3",
50
- "SCREEN_SHARE_PROFILE": "1080p_2",
51
- "ICON_TEXT": true,
52
- "PRIMARY_ACTION_BRAND_COLOR": "#099DFD",
53
- "PRIMARY_ACTION_TEXT_COLOR": "#FFFFFF",
54
- "SECONDARY_ACTION_COLOR": "#FFFFFF",
55
- "FONT_COLOR": "#FFFFFF",
56
- "BACKGROUND_COLOR": "#111111",
57
- "VIDEO_AUDIO_TILE_COLOR": "#222222",
58
- "VIDEO_AUDIO_TILE_OVERLAY_COLOR": "#000004",
59
- "VIDEO_AUDIO_TILE_TEXT_COLOR": "#FFFFFF",
60
- "VIDEO_AUDIO_TILE_AVATAR_COLOR": "#BDD0DB",
61
- "SEMANTIC_ERROR": "#FF414D",
62
- "SEMANTIC_SUCCESS": "#36B37E",
63
- "SEMANTIC_WARNING": "#FFAB00",
64
- "SEMANTIC_NEUTRAL": "#808080",
65
- "INPUT_FIELD_BACKGROUND_COLOR": "#181818",
66
- "INPUT_FIELD_BORDER_COLOR": "#262626",
67
- "CARD_LAYER_1_COLOR": "#1D1D1D",
68
- "CARD_LAYER_2_COLOR": "#262626",
69
- "CARD_LAYER_3_COLOR": "#2D2D2D",
70
- "CARD_LAYER_4_COLOR": "#333333",
71
- "CARD_LAYER_5_COLOR": "#808080",
72
- "HARD_CODED_BLACK_COLOR": "#000000",
73
- "ICON_BG_COLOR": "#242529",
74
- "TOOLBAR_COLOR": "#111111",
75
- "ACTIVE_SPEAKER": true,
76
- "ENABLE_IDP_AUTH": false,
77
- "ENABLE_TOKEN_AUTH": false,
78
- "ENABLE_STT": true,
79
- "ENABLE_CAPTION": true,
80
- "ENABLE_MEETING_TRANSCRIPT": true,
81
- "ENABLE_NOISE_CANCELLATION": true,
82
- "ENABLE_VIRTUAL_BACKGROUND": true,
83
- "ENABLE_WHITEBOARD": true,
84
- "ENABLE_WHITEBOARD_FILE_UPLOAD": false,
85
- "ENABLE_WAITING_ROOM": false,
86
- "WHITEBOARD_APPIDENTIFIER": "EEJBQPVbEe2Bao8ZShuoHQ/hgB5eo0qcDbVig",
87
- "WHITEBOARD_REGION": "us-sv",
88
- "CHAT_ORG_NAME": "41754367",
89
- "CHAT_APP_NAME": "1042822",
90
- "CHAT_URL": "https://a41.chat.agora.io",
91
- "ENABLE_NOISE_CANCELLATION_BY_DEFAULT": true,
92
- "DISABLE_LANDSCAPE_MODE": false,
93
- "STT_AUTO_START": false,
94
- "CLOUD_RECORDING_AUTO_START": false,
95
- "ENABLE_SPOTLIGHT": false,
96
- "ENABLE_LIVE_REACTIONS": true,
97
- "AUTO_CONNECT_RTM": false,
98
- "ENABLE_CONVERSATIONAL_AI": false,
99
- "CUSTOMIZE_AGENT": false,
100
- "ENABLE_TEXT_TRACKS": true
101
- }
@@ -1,91 +0,0 @@
1
- {
2
- "layoutProps": {"topPinned": false},
3
- "primaryButton":{
4
- "width": "100%",
5
- "backgroundColor": "#00AEFC",
6
- "borderRadius": 100,
7
- "maxWidth": 450,
8
- "minWidth": 200,
9
- "minHeight": 45
10
- },
11
- "primaryButtonText": {
12
- "width": "100%",
13
- "height": 45,
14
- "lineHeight": 45,
15
- "fontSize": 16,
16
- "textAlign": "center",
17
- "textAlignVertical": "center",
18
- "color": "#fff"
19
- },
20
- "secondaryBtn": {
21
- "width": "100%",
22
- "borderColor": "#00AEFC",
23
- "borderWidth": 1,
24
- "borderRadius": 100,
25
- "maxWidth": 450,
26
- "minWidth": 200,
27
- "minHeight": 45
28
- },
29
- "secondaryButtonText": {
30
- "width": "100%",
31
- "height": 45,
32
- "lineHeight": 45,
33
- "fontSize": 16,
34
- "textAlign": "center",
35
- "fontWeight": "500",
36
- "textAlignVertical": "center",
37
- "color": "#000"
38
- },
39
- "textInput": {
40
- "width": "100%",
41
- "paddingLeft": 8,
42
- "paddingRight": 8,
43
- "borderColor": "#00AEFC",
44
- "borderRadius": 100,
45
- "borderWidth": 1,
46
- "textAlign": "center",
47
- "color": "#333333",
48
- "fontSize": 16,
49
- "maxWidth": 450,
50
- "minHeight": 45
51
- },
52
- "videoView": {
53
- "flex": 12,
54
- "backgroundColor": "#fff",
55
- "flexDirection": "row"
56
- },
57
- "navHolder": {
58
- "width": "100%",
59
- "height": "6%",
60
- "minHeight": 40,
61
- "backgroundColor": "#f1f4f9",
62
- "flexDirection": "row",
63
- "alignItems": "center",
64
- "justifyContent": "flex-end"
65
- },
66
- "controlsHolder": {
67
- "minHeight": 80,
68
- "maxHeight": "8%",
69
- "backgroundColor": "#f1f4f9",
70
- "flexDirection": "row",
71
- "justifyContent": "space-evenly",
72
- "position": "relative",
73
- "margin": 0,
74
- "bottom": 0
75
- },
76
- "localButton": {
77
- "backgroundColor": "#fff",
78
- "borderRadius": 23,
79
- "width": 40,
80
- "height": 40,
81
- "display": "flex",
82
- "alignSelf": "center",
83
- "alignItems": "center",
84
- "justifyContent": "center"
85
- },
86
- "buttonIcon": {
87
- "width": "70%",
88
- "height": "70%",
89
- "tintColor": "#00AEFC"
90
- }
91
- }