agora-appbuilder-core 4.0.35 → 4.1.0-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 (75) hide show
  1. package/package.json +2 -2
  2. package/template/agora-rn-uikit/README.md +1 -40
  3. package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -0
  4. package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -0
  5. package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +11 -0
  6. package/template/agora-rn-uikit/src/Reducer/index.ts +1 -0
  7. package/template/agora-rn-uikit/src/RtcConfigure.tsx +7 -0
  8. package/template/bridge/rtc/webNg/RtcEngine.ts +4 -1
  9. package/template/customization-api/app-state.ts +11 -7
  10. package/template/customization-api/{customize.ts → customize.tsx} +116 -11
  11. package/template/customization-api/sub-components.ts +4 -0
  12. package/template/customization-api/temp.ts +2 -0
  13. package/template/customization-api/typeDefinition.ts +2 -1
  14. package/template/customization-api/utils.ts +6 -1
  15. package/template/defaultConfig.js +4 -2
  16. package/template/global.d.ts +2 -0
  17. package/template/src/AppRoutes.tsx +15 -5
  18. package/template/src/ai-agent/components/AgentControls/AgentContext.tsx +163 -0
  19. package/template/src/ai-agent/components/AgentControls/LeaveCall.png +0 -0
  20. package/template/src/ai-agent/components/AgentControls/Vector.svg +3 -0
  21. package/template/src/ai-agent/components/AgentControls/const.ts +58 -0
  22. package/template/src/ai-agent/components/AgentControls/index.tsx +293 -0
  23. package/template/src/ai-agent/components/AudioVisualizer.tsx +91 -0
  24. package/template/src/ai-agent/components/Bottombar.tsx +91 -0
  25. package/template/src/ai-agent/components/CustomCreate.tsx +279 -0
  26. package/template/src/ai-agent/components/CustomCreateNative.tsx +265 -0
  27. package/template/src/ai-agent/components/CustomSidePanel.tsx +135 -0
  28. package/template/src/ai-agent/components/FallbackLogo.tsx +80 -0
  29. package/template/src/ai-agent/components/LocalAudioWave.tsx +171 -0
  30. package/template/src/ai-agent/components/agent-chat-panel/agent-chat-ui.tsx +82 -0
  31. package/template/src/ai-agent/components/icons.tsx +227 -0
  32. package/template/src/ai-agent/components/mobile/Bottombar.tsx +47 -0
  33. package/template/src/ai-agent/components/mobile/MobileLayoutComponent.tsx +106 -0
  34. package/template/src/ai-agent/components/mobile/Topbar.tsx +62 -0
  35. package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/LiveAudioVisualizer.tsx +173 -0
  36. package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/index.ts +1 -0
  37. package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/utils.ts +102 -0
  38. package/template/src/ai-agent/components/react-audio-visualize/index.ts +1 -0
  39. package/template/src/ai-agent/components/utils.ts +15 -0
  40. package/template/src/ai-agent/index.tsx +301 -0
  41. package/template/src/ai-agent/routes/CustomLoginRoute.tsx +25 -0
  42. package/template/src/ai-agent/routes/CustomValidateRoute.tsx +25 -0
  43. package/template/src/ai-agent/utils.ts +78 -0
  44. package/template/src/assets/font-styles.css +4 -0
  45. package/template/src/assets/fonts/icomoon.ttf +0 -0
  46. package/template/src/assets/selection.json +1 -1
  47. package/template/src/atoms/CustomIcon.tsx +1 -0
  48. package/template/src/atoms/ImageIcon.tsx +3 -0
  49. package/template/src/atoms/ToolbarItem.tsx +0 -2
  50. package/template/src/components/ChatContext.ts +7 -0
  51. package/template/src/components/Controls.tsx +6 -1
  52. package/template/src/components/ErrorBoundary.tsx +37 -0
  53. package/template/src/components/ErrorBoundaryFallback.tsx +44 -0
  54. package/template/src/components/RTMConfigure.tsx +25 -20
  55. package/template/src/components/participants/Participant.tsx +4 -0
  56. package/template/src/components/participants/UserActionMenuOptions.tsx +34 -1
  57. package/template/src/components/precall/PermissionHelper.tsx +11 -8
  58. package/template/src/language/default-labels/videoCallScreenLabels.ts +8 -0
  59. package/template/src/logger/AppBuilderLogger.tsx +4 -1
  60. package/template/src/pages/Create.tsx +11 -12
  61. package/template/src/pages/VideoCall.tsx +1 -0
  62. package/template/src/pages/video-call/ActionSheet.tsx +33 -29
  63. package/template/src/pages/video-call/SidePanelHeader.tsx +8 -3
  64. package/template/src/pages/video-call/SpotlightHighlighter.tsx +91 -0
  65. package/template/src/pages/video-call/VideoCallMobileView.tsx +17 -6
  66. package/template/src/pages/video-call/VideoCallScreen.tsx +0 -1
  67. package/template/src/pages/video-call/VideoRenderer.tsx +32 -4
  68. package/template/src/rtm-events/constants.ts +2 -0
  69. package/template/src/subComponents/ChatBubble.tsx +34 -15
  70. package/template/src/subComponents/FallbackLogo.tsx +3 -1
  71. package/template/src/subComponents/LocalAudioMute.tsx +20 -2
  72. package/template/src/utils/index.tsx +3 -4
  73. package/template/src/utils/useJoinRoom.ts +14 -0
  74. package/template/src/utils/useSpotlight.ts +31 -0
  75. package/template/tsconfig.json +23 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.0.35",
3
+ "version": "4.1.0-beta-2",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -9,7 +9,7 @@
9
9
  ],
10
10
  "scripts": {
11
11
  "vercel-build": "npm run dev-setup && cd template && npm run web:build && cd .. && npm run copy-vercel",
12
- "uikit": "rm -rf template/agora-rn-uikit && git clone https://github.com/AgoraIO-Community/ReactNative-UIKit.git template/agora-rn-uikit && cd template/agora-rn-uikit && git checkout appbuilder-uikit-3.0.29",
12
+ "uikit": "rm -rf template/agora-rn-uikit && git clone https://github.com/AgoraIO-Community/appbuilder-ui-kit.git template/agora-rn-uikit && cd template/agora-rn-uikit && git checkout appbuilder-uikit-3.0.36",
13
13
  "deps": "cd template && npm i --force",
14
14
  "dev-setup": "npm run uikit && npm run deps && node devSetup.js",
15
15
  "web-build": "cd template && npm run web:build && cd .. && npm run copy-vercel",
@@ -1,42 +1,3 @@
1
1
  <div style="text-align:center">
2
- <h1> Agora React Native UIKit</h1>
3
- <h6>Rapidly integrate video calling into your React Native applications with built in UI Elements.</h6>
2
+ <h1> AppBuilder UIKit - Internal Usage</h1>
4
3
  </div>
5
-
6
- ## Getting started
7
-
8
- ### Installation
9
-
10
- To a react-native application generated using react-native-cli, add the following:
11
-
12
- ```
13
- npm i react-native-agora agora-rn-uikit
14
- ```
15
-
16
- ### Usage
17
-
18
- This UIKit is very simple to use and contains a high level component called `AgoraUIKit`.
19
-
20
- **A simple sample app integrating Agora UI Kit:**
21
- ```javascript
22
- import React, { useState } from 'react';
23
- import AgoraUIKit from 'agora-rn-uikit';
24
-
25
- const App = () => {
26
- const [videoCall, setVideoCall] = useState(true);
27
- const rtcProps = {
28
- appId: '<-----App ID here----->',
29
- channel: 'test',
30
- };
31
- const callbacks = {
32
- EndCall: () => setVideoCall(false),
33
- };
34
- return videoCall ? (
35
- <AgoraUIKit rtcProps={rtcProps} callbacks={callbacks} />
36
- ) : (
37
- <></>
38
- );
39
- };
40
-
41
- export default App;
42
- ```
@@ -185,6 +185,7 @@ export interface CallbacksInterface {
185
185
  AddCustomContent(uid: UidType, data: any): void;
186
186
  RemoveCustomContent(uid: UidType): void;
187
187
  UserPin(Uid: UidType): void;
188
+ Spotlight(Uid: UidType): void;
188
189
  UserSecondaryPin(Uid: UidType): void;
189
190
  ActiveSpeaker(Uid: UidType): void;
190
191
  }
@@ -33,6 +33,7 @@ export interface ContentStateInterface {
33
33
  pinnedUid?: UidType;
34
34
  secondaryPinnedUid?: UidType;
35
35
  lastJoinedUid?: UidType;
36
+ spotlightUid?: UidType;
36
37
  }
37
38
 
38
39
  export interface ActionInterface<T extends keyof CallbacksInterface> {
@@ -0,0 +1,11 @@
1
+ import {ActionType, ContentStateInterface} from '../Contexts/RtcContext';
2
+
3
+ export default function Spotlight(
4
+ state: ContentStateInterface,
5
+ action: ActionType<'Spotlight'>,
6
+ ) {
7
+ return {
8
+ ...state,
9
+ spotlightUid: action?.value && action.value?.length ? action.value[0] : 0,
10
+ };
11
+ }
@@ -11,3 +11,4 @@ export {default as RemoteVideoStateChanged} from './RemoteVideoStateChanged';
11
11
  export {default as UserPin} from './UserPin';
12
12
  export {default as UserSecondaryPin} from './UserSecondaryPin';
13
13
  export {default as ActiveSpeaker} from './ActiveSpeaker';
14
+ export {default as Spotlight} from './Spotlight';
@@ -32,6 +32,7 @@ import {
32
32
  UserPin,
33
33
  UserSecondaryPin,
34
34
  ActiveSpeaker,
35
+ Spotlight,
35
36
  } from './Reducer';
36
37
  import Create from './Rtc/Create';
37
38
  import Join from './Rtc/Join';
@@ -260,6 +261,11 @@ const RtcConfigure = (outerProps: {children: React.ReactNode}) => {
260
261
  stateUpdate = ActiveSpeaker(state, action);
261
262
  }
262
263
  break;
264
+ case 'Spotlight':
265
+ if (actionTypeGuard(action, action.type)) {
266
+ stateUpdate = Spotlight(state, action);
267
+ }
268
+ break;
263
269
  }
264
270
 
265
271
  // TODO: remove Handle event listeners
@@ -459,6 +465,7 @@ const RtcConfigure = (outerProps: {children: React.ReactNode}) => {
459
465
  ? uidState.secondaryPinnedUid
460
466
  : undefined,
461
467
  lastJoinedUid: uidState.lastJoinedUid,
468
+ spotlightUid: uidState.spotlightUid,
462
469
  }}>
463
470
  {outerProps.children}
464
471
  </ContentProvider>
@@ -220,7 +220,7 @@ export default class RtcEngine {
220
220
  private activeSpeakerUid: number;
221
221
  public appId: string;
222
222
  // public AgoraRTC: any;
223
- public client: any | IAgoraRTCClient;
223
+ public client: IAgoraRTCClient;
224
224
  public screenClient: any | IAgoraRTCClient;
225
225
  public eventsMap = new Map<string, callbackType>([
226
226
  ['onUserJoined', () => null],
@@ -630,6 +630,7 @@ export default class RtcEngine {
630
630
  }
631
631
 
632
632
  async publish() {
633
+ console.log(`Audio-Track: ${this.localStream.audio}`);
633
634
  if (this.localStream.audio || this.localStream.video) {
634
635
  try {
635
636
  let tracks: Array<ILocalTrack> = [];
@@ -646,7 +647,9 @@ export default class RtcEngine {
646
647
  'API',
647
648
  'RTC [publish] trying to publish tracks',
648
649
  );
650
+ console.log(`Audio-Track: RTC publish ${this.localStream.audio}`);
649
651
  await this.client.publish(tracks);
652
+ console.log(`Audio-Track: RTC published ${this.localStream.audio}`);
650
653
  logger.log(
651
654
  LogSource.AgoraSDK,
652
655
  'API',
@@ -4,9 +4,9 @@
4
4
  import {createHook} from 'customization-implementation';
5
5
  import {RtcContext, ContentContext} from '../agora-rn-uikit';
6
6
 
7
- // commented for v1 release
8
- //import {default as DeviceContext} from '../src/components/DeviceContext';
9
- //import {default as StorageContext} from '../src/components/StorageContext';
7
+ import {default as DeviceContext} from '../src/components/DeviceContext';
8
+ import {default as StorageContext} from '../src/components/StorageContext';
9
+ import {ErrorContext} from '../src/components/common/Error';
10
10
  /**
11
11
  * The RTC app state exposes the internal RtcEngine object as well as dispatch interface to perform various actions.
12
12
  */
@@ -18,9 +18,9 @@ export const useContent = createHook(ContentContext);
18
18
 
19
19
  export {useLocalUserInfo} from '../src/app-state/useLocalUserInfo';
20
20
 
21
- // commented for v1 release
22
- //export const useDeviceContext = createHook(DeviceContext);
23
- //export const useStorageContext = createHook(StorageContext);
21
+ export const useDeviceContext = createHook(DeviceContext);
22
+ export const useStorageContext = createHook(StorageContext);
23
+ export const useErrorContext = createHook(ErrorContext);
24
24
 
25
25
  /**
26
26
  * UI contexts
@@ -40,8 +40,12 @@ export type {LayoutContextInterface} from '../src/utils/useLayout';
40
40
  // export type {ScreenshareContextInterface} from '../src/subComponents/screenshare/useScreenshare';
41
41
  export {useRecording} from '../src/subComponents/recording/useRecording';
42
42
  export type {RecordingContextInterface} from '../src/subComponents/recording/useRecording';
43
- export {useRoomInfo} from '../src/components/room-info/useRoomInfo';
43
+ export {
44
+ useRoomInfo,
45
+ RoomInfoDefaultValue,
46
+ } from '../src/components/room-info/useRoomInfo';
44
47
  export type {RoomInfoContextInterface} from '../src/components/room-info/useRoomInfo';
48
+ export {useSetRoomInfo} from '../src/components/room-info/useSetRoomInfo';
45
49
  export {useMessages} from '../src/app-state/useMessages';
46
50
  export type {messageInterface} from '../src/app-state/useMessages';
47
51
  export {SidePanelType} from '../src/subComponents/SidePanelEnum';
@@ -9,6 +9,8 @@
9
9
  information visit https://appbuilder.agora.io.
10
10
  *********************************************
11
11
  */
12
+ import React from 'react';
13
+ import {AI_AGENT_CUSTOMIZATION} from '../src/ai-agent';
12
14
  import {LogSource, logger} from '../src/logger/AppBuilderLogger';
13
15
  import {CustomizationApiInterface} from './typeDefinition';
14
16
  import ReactIs from 'react-is';
@@ -129,20 +131,123 @@ function validatei18n(data: any) {
129
131
  }
130
132
  }
131
133
  }
134
+
135
+ const mergeCustomization = (
136
+ externalConfig: CustomizationApiInterface,
137
+ aiAgentConfig: CustomizationApiInterface,
138
+ ) => {
139
+ //check if any external config passed
140
+ if (
141
+ !externalConfig ||
142
+ (externalConfig && !Object.keys(externalConfig)?.length)
143
+ ) {
144
+ logger.log(
145
+ LogSource.CustomizationAPI,
146
+ 'AI_AGENT_CUSTOMIZATION',
147
+ 'Applied default customization',
148
+ );
149
+ //if not then return the aiAgentConfig
150
+ return aiAgentConfig;
151
+ }
152
+
153
+ //merging config
154
+ const mergedData: CustomizationApiInterface = mergeDeep(
155
+ aiAgentConfig,
156
+ externalConfig,
157
+ );
158
+
159
+ logger.log(
160
+ LogSource.CustomizationAPI,
161
+ 'EXTERNAL_CUSTOMIZATION',
162
+ 'Applied EXTERNAL_CUSTOMIZATION with AI_AGENT_CUSTOMZATION',
163
+ );
164
+ //override the app root
165
+ if (externalConfig?.components?.appRoot) {
166
+ const AiAgentAppRoot = aiAgentConfig.components.appRoot;
167
+ const ExternalAppRoot = externalConfig.components.appRoot;
168
+ mergedData.components.appRoot = props => (
169
+ <AiAgentAppRoot>
170
+ <ExternalAppRoot>{props.children}</ExternalAppRoot>
171
+ </AiAgentAppRoot>
172
+ );
173
+ logger.log(
174
+ LogSource.CustomizationAPI,
175
+ 'EXTERNAL_CUSTOMIZATION',
176
+ 'Applied appRoot with aiAgent appRoot',
177
+ );
178
+ }
179
+
180
+ //override the i18n
181
+ if (externalConfig?.i18n && externalConfig?.i18n?.length) {
182
+ mergedData.i18n = externalConfig.i18n;
183
+ } else if (
184
+ (!externalConfig?.i18n || !externalConfig?.i18n?.length) &&
185
+ aiAgentConfig?.i18n?.length
186
+ ) {
187
+ mergedData.i18n = aiAgentConfig.i18n;
188
+ }
189
+
190
+ return mergedData;
191
+ };
192
+
132
193
  export const customize = (config: CustomizationApiInterface) => {
133
- //validating the components
134
- config?.components && validateComponents(config.components);
194
+ let newConfig: CustomizationApiInterface = {};
135
195
 
136
- //validating the custom routes
137
- //commented for v1 release
138
- //config?.customRoutes && validateCustomRoutes(config.customRoutes);
196
+ try {
197
+ //check if is ai agent and merge agent and user config
198
+ if ($config.ENABLE_CONVERSATIONAL_AI) {
199
+ newConfig = mergeCustomization(config, AI_AGENT_CUSTOMIZATION);
200
+ } else {
201
+ newConfig = config;
202
+ }
139
203
 
140
- //validating the i18n
141
- config?.i18n && validatei18n(config.i18n);
204
+ //validating the components
205
+ newConfig?.components && validateComponents(newConfig.components);
142
206
 
143
- //validating the lifecycle
144
- //commented for v1 release
145
- //config?.lifecycle && validateLifecycle(config?.lifecycle);
207
+ //validating the custom routes
208
+ config?.customRoutes && validateCustomRoutes(config.customRoutes);
146
209
 
147
- return config;
210
+ //validating the i18n
211
+ newConfig?.i18n && validatei18n(newConfig.i18n);
212
+
213
+ //validating the lifecycle
214
+ config?.lifecycle && validateLifecycle(config?.lifecycle);
215
+ } catch (error) {
216
+ logger.error(
217
+ LogSource.CustomizationAPI,
218
+ 'Log',
219
+ 'Error on applying the customization',
220
+ error,
221
+ );
222
+ }
223
+
224
+ return newConfig;
148
225
  };
226
+
227
+ /**
228
+ * Performs a deep merge of objects and returns new object. Does not modify
229
+ * objects (immutable) and merges arrays via concatenation.
230
+ *
231
+ * @param {...object} objects - Objects to merge
232
+ * @returns {object} New object with merged key/values
233
+ */
234
+ function mergeDeep(...objects) {
235
+ const isObject = obj => obj && typeof obj === 'object';
236
+
237
+ return objects.reduce((prev, obj) => {
238
+ Object.keys(obj).forEach(key => {
239
+ const pVal = prev[key];
240
+ const oVal = obj[key];
241
+
242
+ if (Array.isArray(pVal) && Array.isArray(oVal)) {
243
+ prev[key] = pVal.concat(...oVal);
244
+ } else if (isObject(pVal) && isObject(oVal)) {
245
+ prev[key] = mergeDeep(pVal, oVal);
246
+ } else {
247
+ prev[key] = oVal;
248
+ }
249
+ });
250
+
251
+ return prev;
252
+ }, {});
253
+ }
@@ -87,3 +87,7 @@ export {default as CaptionPanel} from '../src/subComponents/caption/CaptionConta
87
87
  export {default as VBPreview} from '../src/components/virtual-background/VideoPreview';
88
88
  export {default as Toast} from '../react-native-toast-message';
89
89
  export {default as CaptionContainer} from '../src/subComponents/caption/CaptionContainer';
90
+ export {default as Loading} from '../src/subComponents/Loading';
91
+ export {default as UserAvatar} from '../src/atoms/UserAvatar';
92
+ export {default as Card} from '../src/atoms/Card';
93
+ export {default as ThemeConfig} from '../src/theme';
@@ -2,6 +2,7 @@
2
2
 
3
3
  import VideoRenderer from '../src/pages/video-call/VideoRenderer';
4
4
  import {DispatchContext} from '../agora-rn-uikit';
5
+ import IconButton from '../src/atoms/IconButton';
5
6
  import WhiteboardView from '../src/components/whiteboard/WhiteboardView';
6
7
  import {
7
8
  useWhiteboard,
@@ -27,6 +28,7 @@ import useEndCall from '../src/utils/useEndCall';
27
28
  export {
28
29
  VideoRenderer,
29
30
  DispatchContext,
31
+ IconButton,
30
32
  WhiteboardView,
31
33
  whiteboardContext,
32
34
  useVideoCall,
@@ -100,7 +100,7 @@ export type ComponentsInterface = {
100
100
  precall?: PreCallInterface;
101
101
  preferenceWrapper?: React.ComponentType;
102
102
  //precall?: React.ComponentType;
103
- //create?: React.ComponentType;
103
+ create?: React.ComponentType;
104
104
  //share?: React.ComponentType;
105
105
  //join?: React.ComponentType;
106
106
  videoCall?: VideoCallInterface;
@@ -114,6 +114,7 @@ export interface CustomRoutesInterface {
114
114
  isPrivateRoute?: boolean;
115
115
  routeProps?: object;
116
116
  failureRedirectTo?: string;
117
+ isTopLevelRoute?: boolean;
117
118
  }
118
119
 
119
120
  export type CustomHookType = () => () => Promise<void>;
@@ -25,7 +25,7 @@ export {default as useIsVideoEnabled} from '../src/utils/useIsVideoEnabled';
25
25
  export {default as useActiveSpeaker} from '../src/utils/useActiveSpeaker';
26
26
 
27
27
  //hooks used for navigation
28
- export {useHistory, useParams} from '../src/components/Router';
28
+ export {useHistory, useParams, Redirect} from '../src/components/Router';
29
29
 
30
30
  //export common function
31
31
  export {
@@ -34,7 +34,11 @@ export {
34
34
  isAndroid,
35
35
  isDesktop,
36
36
  calculatePosition,
37
+ isWebInternal,
38
+ trimText,
39
+ BREAKPOINTS,
37
40
  } from '../src/utils/common';
41
+ export {default as isSDK} from '../src/utils/isSDK';
38
42
  export {default as isMobileOrTablet} from '../src/utils/isMobileOrTablet';
39
43
  export {useLocalUid} from '../agora-rn-uikit';
40
44
  export {default as useLocalAudio} from '../src/utils/useLocalAudio';
@@ -49,4 +53,5 @@ export {useFullScreen} from '../src/utils/useFullScreen';
49
53
  export {useHideShareTitle} from '../src/utils/useHideShareTile';
50
54
  export {useActionSheet} from '../src/utils/useActionSheet';
51
55
  export {default as PlatformWrapper} from '../src/utils/PlatformWrapper';
56
+ export {useSpotlight} from '../src/utils/useSpotlight';
52
57
  export {useActiveUids} from '../src/utils/useActiveUids';
@@ -76,12 +76,14 @@ const DefaultConfig = {
76
76
  CHAT_ORG_NAME: '',
77
77
  CHAT_APP_NAME: '',
78
78
  CHAT_URL: '',
79
- CLI_VERSION: '3.0.35',
80
- CORE_VERSION: '4.0.35',
79
+ CLI_VERSION: '3.1.0-beta-2',
80
+ CORE_VERSION: '4.1.0-beta-2',
81
81
  DISABLE_LANDSCAPE_MODE: false,
82
82
  STT_AUTO_START: false,
83
83
  CLOUD_RECORDING_AUTO_START: false,
84
+ ENABLE_SPOTLIGHT: false,
84
85
  AUTO_CONNECT_RTM: false,
86
+ ENABLE_CONVERSATIONAL_AI: false,
85
87
  };
86
88
 
87
89
  module.exports = DefaultConfig;
@@ -168,7 +168,9 @@ interface ConfigInterface {
168
168
  DISABLE_LANDSCAPE_MODE: boolean;
169
169
  STT_AUTO_START: boolean;
170
170
  CLOUD_RECORDING_AUTO_START: boolean;
171
+ ENABLE_SPOTLIGHT: boolean;
171
172
  AUTO_CONNECT_RTM: boolean;
173
+ ENABLE_CONVERSATIONAL_AI: boolean;
172
174
  }
173
175
  declare var $config: ConfigInterface;
174
176
  declare module 'customization' {
@@ -9,7 +9,7 @@
9
9
  information visit https://appbuilder.agora.io.
10
10
  *********************************************
11
11
  */
12
- import React, {useEffect} from 'react';
12
+ import React from 'react';
13
13
  import Join from './pages/Join';
14
14
  import VideoCall from './pages/VideoCall';
15
15
  import Create from './pages/Create';
@@ -22,18 +22,24 @@ import {CUSTOM_ROUTES_PREFIX, CustomRoutesInterface} from 'customization-api';
22
22
  import PrivateRoute from './components/PrivateRoute';
23
23
  import RecordingBotRoute from './components/recording-bot/RecordingBotRoute';
24
24
  import {useIsRecordingBot} from './subComponents/recording/useIsRecordingBot';
25
- import {LogSource, logger} from './logger/AppBuilderLogger';
26
25
  import {isValidReactComponent} from './utils/common';
26
+ import ErrorBoundary from './components/ErrorBoundary';
27
+ import {ErrorBoundaryFallback} from './components/ErrorBoundaryFallback';
27
28
 
28
29
  function VideoCallWrapper(props) {
29
30
  const {isRecordingBot} = useIsRecordingBot();
31
+ const ErrorBoundaryFallbackComponent = <ErrorBoundaryFallback />;
30
32
  return isRecordingBot ? (
31
33
  <RecordingBotRoute history={props.history}>
32
- <VideoCall />
34
+ <ErrorBoundary fallback={ErrorBoundaryFallbackComponent}>
35
+ <VideoCall />
36
+ </ErrorBoundary>
33
37
  </RecordingBotRoute>
34
38
  ) : (
35
39
  <AuthRoute>
36
- <VideoCall />
40
+ <ErrorBoundary fallback={ErrorBoundaryFallbackComponent}>
41
+ <VideoCall />
42
+ </ErrorBoundary>
37
43
  </AuthRoute>
38
44
  );
39
45
  }
@@ -52,7 +58,11 @@ function AppRoutes() {
52
58
  let RouteComponent = item?.isPrivateRoute ? PrivateRoute : Route;
53
59
  return (
54
60
  <RouteComponent
55
- path={CUSTOM_ROUTES_PREFIX + item.path}
61
+ path={
62
+ item.isTopLevelRoute
63
+ ? item.path
64
+ : CUSTOM_ROUTES_PREFIX + item.path
65
+ }
56
66
  exact={item.exact}
57
67
  key={i}
58
68
  failureRedirectTo={
@@ -0,0 +1,163 @@
1
+ import React, {createContext, useState} from 'react';
2
+ import {AIAgentState, AgentState} from './const';
3
+ import {UidType} from 'customization-api';
4
+
5
+ export interface ChatItem {
6
+ id: string;
7
+ uid: UidType;
8
+ text: string;
9
+ isFinal: boolean;
10
+ time: number;
11
+ isSelf: boolean;
12
+ }
13
+
14
+ export interface AgentContextInterface {
15
+ agentConnectionState: AIAgentState;
16
+ setAgentConnectionState: (agentState: AIAgentState) => void;
17
+ agentAuthToken: string | null;
18
+ setAgentAuthToken: (token: string | null) => void;
19
+ isSubscribedForStreams: boolean;
20
+ setIsSubscribedForStreams: (state: boolean) => void;
21
+ agentUID: UidType | null;
22
+ setAgentUID: (uid: UidType | null) => void;
23
+ chatItems: ChatItem[];
24
+ addChatItem: (newItem: ChatItem) => void;
25
+ }
26
+
27
+ export const AgentContext = createContext<AgentContextInterface>({
28
+ agentConnectionState: AgentState.NOT_CONNECTED,
29
+ setAgentConnectionState: () => {},
30
+ agentAuthToken: null,
31
+ setAgentAuthToken: () => {},
32
+ isSubscribedForStreams: false,
33
+ setIsSubscribedForStreams: () => {},
34
+ agentUID: null,
35
+ setAgentUID: () => {},
36
+ chatItems: [],
37
+ addChatItem: () => {}, // Default no-op
38
+ });
39
+
40
+ /**
41
+ * Helper function to find the correct insertion index for a new item using binary search.
42
+ * Ensures that the array remains sorted by the `time` property after insertion.
43
+ *
44
+ * @param array The array to search within.
45
+ * @param time The `time` value of the new item to insert.
46
+ * @returns The index where the new item should be inserted.
47
+ */
48
+ const findInsertionIndex = (array: ChatItem[], time: number): number => {
49
+ let low = 0;
50
+ let high = array.length;
51
+
52
+ // Perform binary search to find the insertion index
53
+ while (low < high) {
54
+ const mid = Math.floor((low + high) / 2);
55
+
56
+ // If the middle item's time is less than the new time, search the upper half
57
+ if (array[mid].time < time) {
58
+ low = mid + 1;
59
+ } else {
60
+ // Otherwise, search the lower half
61
+ high = mid;
62
+ }
63
+ }
64
+
65
+ return low; // The correct index for insertion
66
+ };
67
+
68
+ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
69
+ children,
70
+ }) => {
71
+ const [agentConnectionState, setAgentConnectionState] =
72
+ useState<AIAgentState>(AgentState.NOT_CONNECTED);
73
+ const [agentAuthToken, setAgentAuthToken] = useState<string | null>(null);
74
+ const [agentUID, setAgentUID] = useState<UidType | null>(null);
75
+ const [isSubscribedForStreams, setIsSubscribedForStreams] = useState(false);
76
+ const [chatItems, setChatItems] = useState<ChatItem[]>([]);
77
+
78
+ /**
79
+ * Adds a new chat item to the chat state while ensuring:
80
+ * - Outdated messages are discarded.
81
+ * - Non-finalized messages are updated if a newer message is received.
82
+ * - Finalized messages are added without duplication.
83
+ * - Chat items remain sorted by their `time` property.
84
+ *
85
+ * @param newItem The new chat item to add.
86
+ */
87
+ const addChatItem = (newItem: ChatItem) => {
88
+ setChatItems(prevItems => {
89
+ // Find the index of the last finalized chat item for the same user
90
+ // Finalized messages are typically considered "complete" and should not be updated by non-final messages
91
+ const LastFinalIndex = prevItems.findLastIndex(
92
+ el => el.uid === newItem.uid && el.isFinal,
93
+ );
94
+
95
+ // Find the index of the last non-finalized chat item for the same user
96
+ // Non-finalized messages represent "in-progress" messages that can be updated or replaced
97
+ const LastNonFinalIndex = prevItems.findLastIndex(
98
+ el => el.uid === newItem.uid && !el.isFinal,
99
+ );
100
+
101
+ // Retrieve the actual items for the indices found above
102
+ const LastFinalItem =
103
+ LastFinalIndex !== -1 ? prevItems[LastFinalIndex] : null;
104
+ const LastNonFinalItem =
105
+ LastNonFinalIndex !== -1 ? prevItems[LastNonFinalIndex] : null;
106
+
107
+ // If the new message's timestamp is older than or equal to the last finalized message,
108
+ // it is considered outdated and discarded to prevent unnecessary overwrites.
109
+ if (LastFinalItem && newItem.time <= LastFinalItem.time) {
110
+ console.log(
111
+ '[AgentProvider] addChatItem - Discarded outdated message:',
112
+ newItem,
113
+ );
114
+ return prevItems; // Return the previous state without changes
115
+ }
116
+
117
+ // Create a new copy of the current chat items to maintain immutability
118
+ let updatedItems = [...prevItems];
119
+
120
+ // If there is a non-finalized message for the same user, replace it with the new message
121
+ if (LastNonFinalItem) {
122
+ console.log(
123
+ '[AgentProvider] addChatItem - Updating non-finalized message:',
124
+ newItem,
125
+ );
126
+ updatedItems[LastNonFinalIndex] = newItem; // Replace the non-finalized message
127
+ } else {
128
+ // If no non-finalized message exists, the new message is added to the array
129
+ console.log(
130
+ '[AgentProvider] addChatItem - Adding new message:',
131
+ newItem,
132
+ );
133
+
134
+ // Use binary search to find the correct insertion index for the new message
135
+ // This ensures the array remains sorted by the `time` property
136
+ const insertIndex = findInsertionIndex(updatedItems, newItem.time);
137
+
138
+ // Insert the new message at the correct position to maintain chronological order
139
+ updatedItems.splice(insertIndex, 0, newItem);
140
+ }
141
+
142
+ // Return the updated array, which will replace the previous state
143
+ return updatedItems;
144
+ });
145
+ };
146
+
147
+ const value = {
148
+ agentConnectionState,
149
+ setAgentConnectionState,
150
+ agentAuthToken,
151
+ setAgentAuthToken,
152
+ isSubscribedForStreams,
153
+ setIsSubscribedForStreams,
154
+ agentUID,
155
+ setAgentUID,
156
+ chatItems,
157
+ addChatItem, // Expose the function in the context
158
+ };
159
+
160
+ return (
161
+ <AgentContext.Provider value={value}>{children}</AgentContext.Provider>
162
+ );
163
+ };
@@ -0,0 +1,3 @@
1
+ <svg width="21" height="22" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path id="Vector" d="M10.496 16.7876C9.35026 16.7876 8.23023 16.4482 7.27757 15.8122C6.3249 15.1763 5.58239 14.2724 5.14393 13.2148C4.70546 12.1573 4.59074 10.9936 4.81427 9.87089C5.03779 8.7482 5.58953 7.71694 6.39971 6.90753C7.20988 6.09811 8.24211 5.54689 9.36585 5.32358C10.4896 5.10026 11.6544 5.21487 12.7129 5.65292C13.7715 6.09098 14.6762 6.83279 15.3128 7.78456C15.9493 8.73633 16.2891 9.85531 16.2891 11C16.2875 12.5345 15.6767 14.0057 14.5906 15.0908C13.5045 16.1758 12.032 16.7861 10.496 16.7876ZM17.2419 2.56827L17.1536 2.68579C17.1049 2.75071 17.0128 2.76384 16.9478 2.71517L16.836 2.62703C15.5071 1.6239 13.9584 0.950884 12.3178 0.663529C10.6773 0.376175 8.99179 0.482718 7.40054 0.974366C5.80929 1.46601 4.35788 2.32867 3.16609 3.49115C1.97429 4.65362 1.07629 6.08258 0.546187 7.6601C0.0160829 9.23762 -0.130923 10.9185 0.117301 12.5639C0.365526 14.2094 1.00186 15.7723 1.9738 17.1237C2.94573 18.475 4.2254 19.5761 5.70719 20.336C7.18898 21.0959 8.83041 21.4929 10.496 21.4941C12.7869 21.5007 15.0158 20.7509 16.836 19.3612L16.9478 19.279C17.0117 19.2279 17.106 19.2418 17.1536 19.3083L17.2419 19.4259C17.6382 19.9723 18.1411 20.433 18.7202 20.7804C19.2993 21.1277 19.9428 21.3545 20.6118 21.4471C20.8172 21.4751 21 21.3155 21 21.1082V0.89175C21 0.684521 20.8172 0.524908 20.6118 0.552882C19.9433 0.645086 19.3003 0.871101 18.7212 1.2174C18.1422 1.56369 17.639 2.02315 17.2419 2.56827Z" fill="#111111"/>
3
+ </svg>