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.
- package/package.json +2 -2
- package/template/agora-rn-uikit/README.md +1 -40
- package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -0
- package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -0
- package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +11 -0
- package/template/agora-rn-uikit/src/Reducer/index.ts +1 -0
- package/template/agora-rn-uikit/src/RtcConfigure.tsx +7 -0
- package/template/bridge/rtc/webNg/RtcEngine.ts +4 -1
- package/template/customization-api/app-state.ts +11 -7
- package/template/customization-api/{customize.ts → customize.tsx} +116 -11
- package/template/customization-api/sub-components.ts +4 -0
- package/template/customization-api/temp.ts +2 -0
- package/template/customization-api/typeDefinition.ts +2 -1
- package/template/customization-api/utils.ts +6 -1
- package/template/defaultConfig.js +4 -2
- package/template/global.d.ts +2 -0
- package/template/src/AppRoutes.tsx +15 -5
- package/template/src/ai-agent/components/AgentControls/AgentContext.tsx +163 -0
- package/template/src/ai-agent/components/AgentControls/LeaveCall.png +0 -0
- package/template/src/ai-agent/components/AgentControls/Vector.svg +3 -0
- package/template/src/ai-agent/components/AgentControls/const.ts +58 -0
- package/template/src/ai-agent/components/AgentControls/index.tsx +293 -0
- package/template/src/ai-agent/components/AudioVisualizer.tsx +91 -0
- package/template/src/ai-agent/components/Bottombar.tsx +91 -0
- package/template/src/ai-agent/components/CustomCreate.tsx +279 -0
- package/template/src/ai-agent/components/CustomCreateNative.tsx +265 -0
- package/template/src/ai-agent/components/CustomSidePanel.tsx +135 -0
- package/template/src/ai-agent/components/FallbackLogo.tsx +80 -0
- package/template/src/ai-agent/components/LocalAudioWave.tsx +171 -0
- package/template/src/ai-agent/components/agent-chat-panel/agent-chat-ui.tsx +82 -0
- package/template/src/ai-agent/components/icons.tsx +227 -0
- package/template/src/ai-agent/components/mobile/Bottombar.tsx +47 -0
- package/template/src/ai-agent/components/mobile/MobileLayoutComponent.tsx +106 -0
- package/template/src/ai-agent/components/mobile/Topbar.tsx +62 -0
- package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/LiveAudioVisualizer.tsx +173 -0
- package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/index.ts +1 -0
- package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/utils.ts +102 -0
- package/template/src/ai-agent/components/react-audio-visualize/index.ts +1 -0
- package/template/src/ai-agent/components/utils.ts +15 -0
- package/template/src/ai-agent/index.tsx +301 -0
- package/template/src/ai-agent/routes/CustomLoginRoute.tsx +25 -0
- package/template/src/ai-agent/routes/CustomValidateRoute.tsx +25 -0
- package/template/src/ai-agent/utils.ts +78 -0
- package/template/src/assets/font-styles.css +4 -0
- package/template/src/assets/fonts/icomoon.ttf +0 -0
- package/template/src/assets/selection.json +1 -1
- package/template/src/atoms/CustomIcon.tsx +1 -0
- package/template/src/atoms/ImageIcon.tsx +3 -0
- package/template/src/atoms/ToolbarItem.tsx +0 -2
- package/template/src/components/ChatContext.ts +7 -0
- package/template/src/components/Controls.tsx +6 -1
- package/template/src/components/ErrorBoundary.tsx +37 -0
- package/template/src/components/ErrorBoundaryFallback.tsx +44 -0
- package/template/src/components/RTMConfigure.tsx +25 -20
- package/template/src/components/participants/Participant.tsx +4 -0
- package/template/src/components/participants/UserActionMenuOptions.tsx +34 -1
- package/template/src/components/precall/PermissionHelper.tsx +11 -8
- package/template/src/language/default-labels/videoCallScreenLabels.ts +8 -0
- package/template/src/logger/AppBuilderLogger.tsx +4 -1
- package/template/src/pages/Create.tsx +11 -12
- package/template/src/pages/VideoCall.tsx +1 -0
- package/template/src/pages/video-call/ActionSheet.tsx +33 -29
- package/template/src/pages/video-call/SidePanelHeader.tsx +8 -3
- package/template/src/pages/video-call/SpotlightHighlighter.tsx +91 -0
- package/template/src/pages/video-call/VideoCallMobileView.tsx +17 -6
- package/template/src/pages/video-call/VideoCallScreen.tsx +0 -1
- package/template/src/pages/video-call/VideoRenderer.tsx +32 -4
- package/template/src/rtm-events/constants.ts +2 -0
- package/template/src/subComponents/ChatBubble.tsx +34 -15
- package/template/src/subComponents/FallbackLogo.tsx +3 -1
- package/template/src/subComponents/LocalAudioMute.tsx +20 -2
- package/template/src/utils/index.tsx +3 -4
- package/template/src/utils/useJoinRoom.ts +14 -0
- package/template/src/utils/useSpotlight.ts +31 -0
- 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
|
|
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/
|
|
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>
|
|
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
|
}
|
|
@@ -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:
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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 {
|
|
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
|
-
|
|
134
|
-
config?.components && validateComponents(config.components);
|
|
194
|
+
let newConfig: CustomizationApiInterface = {};
|
|
135
195
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
141
|
-
|
|
204
|
+
//validating the components
|
|
205
|
+
newConfig?.components && validateComponents(newConfig.components);
|
|
142
206
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
//config?.lifecycle && validateLifecycle(config?.lifecycle);
|
|
207
|
+
//validating the custom routes
|
|
208
|
+
config?.customRoutes && validateCustomRoutes(config.customRoutes);
|
|
146
209
|
|
|
147
|
-
|
|
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
|
-
|
|
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
|
|
80
|
-
CORE_VERSION: '4.0
|
|
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;
|
package/template/global.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
<
|
|
34
|
+
<ErrorBoundary fallback={ErrorBoundaryFallbackComponent}>
|
|
35
|
+
<VideoCall />
|
|
36
|
+
</ErrorBoundary>
|
|
33
37
|
</RecordingBotRoute>
|
|
34
38
|
) : (
|
|
35
39
|
<AuthRoute>
|
|
36
|
-
<
|
|
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={
|
|
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
|
+
};
|
|
Binary file
|
|
@@ -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>
|