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
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {isMobileUA} from '../../../utils/common';
|
|
2
|
+
|
|
3
|
+
export const AI_AGENT_STATE = {
|
|
4
|
+
NOT_CONNECTED: 'Start Call',
|
|
5
|
+
REQUEST_SENT: isMobileUA() ? 'Start Call' : 'Requesting agent join..', // loading - reg
|
|
6
|
+
AWAITING_JOIN: isMobileUA() ? 'Start Call' : 'Agent will join shortly..', // loading
|
|
7
|
+
AGENT_CONNECTED: 'End Call',
|
|
8
|
+
AGENT_REQUEST_FAILED: 'Start Call',
|
|
9
|
+
AGENT_DISCONNECT_REQUEST: isMobileUA()
|
|
10
|
+
? 'End Call'
|
|
11
|
+
: 'Disconnecting agent...', // loading - req
|
|
12
|
+
AGENT_DISCONNECT_FAILED: 'End Call',
|
|
13
|
+
AWAITING_LEAVE: 'Start Call', // loading
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
export type AIAgentState = keyof typeof AI_AGENT_STATE;
|
|
17
|
+
|
|
18
|
+
export const AGENT_STATE_TO_API_ACTION = {
|
|
19
|
+
NOT_CONNECTED: 'start_agent',
|
|
20
|
+
AGENT_CONNECTED: 'stop_agent',
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
export type AgentStateToApiAction = keyof typeof AGENT_STATE_TO_API_ACTION;
|
|
24
|
+
|
|
25
|
+
export const enum AgentState {
|
|
26
|
+
NOT_CONNECTED = 'NOT_CONNECTED',
|
|
27
|
+
REQUEST_SENT = 'REQUEST_SENT',
|
|
28
|
+
AWAITING_JOIN = 'AWAITING_JOIN',
|
|
29
|
+
AGENT_CONNECTED = 'AGENT_CONNECTED',
|
|
30
|
+
AGENT_REQUEST_FAILED = 'AGENT_REQUEST_FAILED',
|
|
31
|
+
AGENT_DISCONNECT_REQUEST = 'AGENT_DISCONNECT_REQUEST',
|
|
32
|
+
AGENT_DISCONNECT_FAILED = 'AGENT_DISCONNECT_FAILED',
|
|
33
|
+
AWAITING_LEAVE = 'AWAITING_LEAVE',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const AI_AGENT_UID = 123456;
|
|
37
|
+
|
|
38
|
+
// export const AGENT_PROXY_URL = "http://localhost:3000/api/proxy"
|
|
39
|
+
// export const AGENT_PROXY_URL = "https://conversational-ai-agent-git-testing-cors-agoraio.vercel.app/api/proxy"
|
|
40
|
+
// export const AGENT_PROXY_URL = "https://nodejs-serverless-function-express-alpha-smoky.vercel.app/api/hello"
|
|
41
|
+
// export const AGENT_PROXY_URL = "https://conversational-ai-agent-git-setmute-agoraio.vercel.app/api/proxy"
|
|
42
|
+
|
|
43
|
+
// production router
|
|
44
|
+
export const AGENT_PROXY_URL =
|
|
45
|
+
'https://agora-realtime-proxy-590d34bfeb04.herokuapp.com';
|
|
46
|
+
// production sso
|
|
47
|
+
export const AGORA_SSO_BASE = 'https://sso2.agora.io';
|
|
48
|
+
|
|
49
|
+
// staging router
|
|
50
|
+
// export const AGENT_PROXY_URL = "https://agora-realtime-proxy-dev-0af5192e12dd.herokuapp.com"
|
|
51
|
+
// staging sso
|
|
52
|
+
// export const AGORA_SSO_BASE = 'https://staging-sso.agora.io';
|
|
53
|
+
|
|
54
|
+
export const AGORA_SSO_LOGIN_PATH = '/api/v0/oauth/authorize';
|
|
55
|
+
|
|
56
|
+
export const AGORA_SSO_LOGOUT_PATH = '/api/v0/logout';
|
|
57
|
+
|
|
58
|
+
export const AGORA_SSO_CLIENT_ID = 'openai_agora';
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import React, {useContext, useEffect} from 'react';
|
|
2
|
+
import {AI_AGENT_STATE, AgentState} from './const';
|
|
3
|
+
import {TouchableOpacity, Text, ActivityIndicator} from 'react-native';
|
|
4
|
+
import {AgentContext} from './AgentContext';
|
|
5
|
+
import {
|
|
6
|
+
ThemeConfig,
|
|
7
|
+
UidType,
|
|
8
|
+
useContent,
|
|
9
|
+
useEndCall,
|
|
10
|
+
useLocalUid,
|
|
11
|
+
useHistory,
|
|
12
|
+
useStorageContext,
|
|
13
|
+
Toast,
|
|
14
|
+
} from 'customization-api';
|
|
15
|
+
import {isMobileUA} from '../../../utils/common';
|
|
16
|
+
import {CallIcon, EndCall} from '../icons';
|
|
17
|
+
|
|
18
|
+
const connectToAIAgent = async (
|
|
19
|
+
agentAction: 'start' | 'stop',
|
|
20
|
+
channel_name: string,
|
|
21
|
+
localUid: UidType,
|
|
22
|
+
agentAuthToken: string,
|
|
23
|
+
): Promise<{}> => {
|
|
24
|
+
// const apiUrl = '/api/proxy';
|
|
25
|
+
const apiUrl = $config.BACKEND_ENDPOINT + '/v1/convoai';
|
|
26
|
+
const requestBody = {
|
|
27
|
+
channel_name: channel_name,
|
|
28
|
+
uid: localUid, // user uid // localUid or 0
|
|
29
|
+
};
|
|
30
|
+
console.log({requestBody});
|
|
31
|
+
const headers: HeadersInit = {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
Authorization: `Bearer ${agentAuthToken}`,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(`${apiUrl}/${agentAction}`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: headers,
|
|
40
|
+
body: JSON.stringify(requestBody),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
|
|
49
|
+
// console.log({data}, "X-Client-ID start stop")
|
|
50
|
+
console.log(
|
|
51
|
+
`AI agent ${agentAction === 'start' ? 'connected' : 'disconnected'}`,
|
|
52
|
+
data,
|
|
53
|
+
);
|
|
54
|
+
if (agentAction === 'start') {
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error(`Failed to ${agentAction} AI agent connection:`, error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const AgentControl: React.FC<{channel_name: string}> = ({
|
|
64
|
+
channel_name,
|
|
65
|
+
}) => {
|
|
66
|
+
const {
|
|
67
|
+
agentConnectionState,
|
|
68
|
+
setAgentConnectionState,
|
|
69
|
+
agentAuthToken,
|
|
70
|
+
setAgentAuthToken,
|
|
71
|
+
agentUID,
|
|
72
|
+
setAgentUID,
|
|
73
|
+
} = useContext(AgentContext);
|
|
74
|
+
// console.log("X-Client-ID state", clientId)
|
|
75
|
+
// const { users } = useContext(UserContext)
|
|
76
|
+
const {activeUids: users} = useContent();
|
|
77
|
+
const endcall = useEndCall();
|
|
78
|
+
const history = useHistory();
|
|
79
|
+
const {store} = useStorageContext();
|
|
80
|
+
const localUid = useLocalUid();
|
|
81
|
+
|
|
82
|
+
// stop_agent API is successful, but agent has not yet left the RTC channel
|
|
83
|
+
const isAwaitingLeave = agentConnectionState === AgentState.AWAITING_LEAVE;
|
|
84
|
+
|
|
85
|
+
console.log(
|
|
86
|
+
'Agent Control--',
|
|
87
|
+
{agentConnectionState},
|
|
88
|
+
{bth: AI_AGENT_STATE[agentConnectionState]},
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const handleConnectionToggle = async () => {
|
|
92
|
+
try {
|
|
93
|
+
// connect to agent when agent is in not connected state or when earlier connect failed
|
|
94
|
+
if (
|
|
95
|
+
agentConnectionState === AgentState.NOT_CONNECTED ||
|
|
96
|
+
agentConnectionState === AgentState.AGENT_REQUEST_FAILED ||
|
|
97
|
+
isAwaitingLeave
|
|
98
|
+
) {
|
|
99
|
+
try {
|
|
100
|
+
setAgentConnectionState(AgentState.REQUEST_SENT);
|
|
101
|
+
const data = await connectToAIAgent(
|
|
102
|
+
'start',
|
|
103
|
+
channel_name,
|
|
104
|
+
localUid,
|
|
105
|
+
store.token,
|
|
106
|
+
);
|
|
107
|
+
// console.log("response X-Client-ID", newClientId, typeof newClientId)
|
|
108
|
+
// @ts-ignore
|
|
109
|
+
const {agent_uid = null} = data;
|
|
110
|
+
|
|
111
|
+
//setClientId(agent_id);
|
|
112
|
+
setAgentUID(agent_uid);
|
|
113
|
+
|
|
114
|
+
setAgentConnectionState(AgentState.AWAITING_JOIN);
|
|
115
|
+
|
|
116
|
+
Toast.show({
|
|
117
|
+
leadingIconName: 'tick-fill',
|
|
118
|
+
type: 'success',
|
|
119
|
+
text1: 'Agent requested to join',
|
|
120
|
+
text2: null,
|
|
121
|
+
visibilityTime: 3000,
|
|
122
|
+
primaryBtn: null,
|
|
123
|
+
secondaryBtn: null,
|
|
124
|
+
leadingIcon: null,
|
|
125
|
+
});
|
|
126
|
+
} catch (agentConnectError) {
|
|
127
|
+
setAgentConnectionState(AgentState.AGENT_REQUEST_FAILED);
|
|
128
|
+
|
|
129
|
+
if (agentConnectError.toString().indexOf('401') !== -1) {
|
|
130
|
+
Toast.show({
|
|
131
|
+
leadingIconName: 'alert',
|
|
132
|
+
type: 'error',
|
|
133
|
+
text1: 'Your session is expired. Please sign in to join call.',
|
|
134
|
+
text2: null,
|
|
135
|
+
visibilityTime: 5000,
|
|
136
|
+
primaryBtn: null,
|
|
137
|
+
secondaryBtn: null,
|
|
138
|
+
leadingIcon: null,
|
|
139
|
+
});
|
|
140
|
+
// window.location.href = '/create'
|
|
141
|
+
await endcall();
|
|
142
|
+
setAgentAuthToken(null);
|
|
143
|
+
return;
|
|
144
|
+
} else {
|
|
145
|
+
Toast.show({
|
|
146
|
+
leadingIconName: 'alert',
|
|
147
|
+
type: 'error',
|
|
148
|
+
text1: 'Uh oh! Agent failed to connect',
|
|
149
|
+
text2: null,
|
|
150
|
+
visibilityTime: 5000,
|
|
151
|
+
primaryBtn: null,
|
|
152
|
+
secondaryBtn: null,
|
|
153
|
+
leadingIcon: null,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
throw agentConnectError;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// disconnect agent with agent is already connected or when earlier disconnect failed
|
|
161
|
+
if (
|
|
162
|
+
agentConnectionState === AgentState.AGENT_CONNECTED ||
|
|
163
|
+
agentConnectionState === AgentState.AGENT_DISCONNECT_FAILED
|
|
164
|
+
) {
|
|
165
|
+
if (isMobileUA()) {
|
|
166
|
+
await endcall();
|
|
167
|
+
setAgentConnectionState(AgentState.NOT_CONNECTED);
|
|
168
|
+
setAgentAuthToken(null);
|
|
169
|
+
return; // check later
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
setAgentConnectionState(AgentState.AGENT_DISCONNECT_REQUEST);
|
|
173
|
+
await connectToAIAgent('stop', channel_name, localUid, store.token);
|
|
174
|
+
setAgentConnectionState(AgentState.AWAITING_LEAVE);
|
|
175
|
+
|
|
176
|
+
Toast.show({
|
|
177
|
+
leadingIconName: 'tick-fill',
|
|
178
|
+
type: 'success',
|
|
179
|
+
text1: 'Agent disconnected',
|
|
180
|
+
text2: null,
|
|
181
|
+
visibilityTime: 3000,
|
|
182
|
+
primaryBtn: null,
|
|
183
|
+
secondaryBtn: null,
|
|
184
|
+
leadingIcon: null,
|
|
185
|
+
});
|
|
186
|
+
} catch (agentDisconnectError) {
|
|
187
|
+
setAgentConnectionState(AgentState.AGENT_DISCONNECT_FAILED);
|
|
188
|
+
|
|
189
|
+
Toast.show({
|
|
190
|
+
leadingIconName: 'alert',
|
|
191
|
+
type: 'error',
|
|
192
|
+
text1: 'Uh oh! Agent failed to disconnect',
|
|
193
|
+
text2: null,
|
|
194
|
+
visibilityTime: 5000,
|
|
195
|
+
primaryBtn: null,
|
|
196
|
+
secondaryBtn: null,
|
|
197
|
+
leadingIcon: null,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
throw agentDisconnectError;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.log(`Agent failed to connect/disconnect - ${error}`);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
console.log('agent contrl', {users});
|
|
210
|
+
// welcome agent
|
|
211
|
+
const aiAgentUID = users.filter(item => item === agentUID);
|
|
212
|
+
if (
|
|
213
|
+
aiAgentUID.length &&
|
|
214
|
+
agentConnectionState === AgentState.AWAITING_JOIN
|
|
215
|
+
) {
|
|
216
|
+
setAgentConnectionState(AgentState.AGENT_CONNECTED);
|
|
217
|
+
|
|
218
|
+
Toast.show({
|
|
219
|
+
leadingIconName: 'tick-fill',
|
|
220
|
+
type: 'success',
|
|
221
|
+
text1: 'Say Hi!!',
|
|
222
|
+
text2: null,
|
|
223
|
+
visibilityTime: 3000,
|
|
224
|
+
primaryBtn: null,
|
|
225
|
+
secondaryBtn: null,
|
|
226
|
+
leadingIcon: null,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// when agent leaves, show left toast, and set agent to not connected state
|
|
230
|
+
if (
|
|
231
|
+
!aiAgentUID.length &&
|
|
232
|
+
agentConnectionState === AgentState.AWAITING_LEAVE
|
|
233
|
+
) {
|
|
234
|
+
setAgentConnectionState(AgentState.NOT_CONNECTED);
|
|
235
|
+
}
|
|
236
|
+
}, [users]);
|
|
237
|
+
|
|
238
|
+
const isLoading =
|
|
239
|
+
agentConnectionState === AgentState.REQUEST_SENT ||
|
|
240
|
+
agentConnectionState === AgentState.AGENT_DISCONNECT_REQUEST ||
|
|
241
|
+
// || agentConnectionState === AgentState.AWAITING_LEAVE
|
|
242
|
+
agentConnectionState === AgentState.AWAITING_JOIN;
|
|
243
|
+
|
|
244
|
+
const isStartAgent =
|
|
245
|
+
agentConnectionState === AgentState.NOT_CONNECTED ||
|
|
246
|
+
agentConnectionState === AgentState.AGENT_REQUEST_FAILED ||
|
|
247
|
+
isAwaitingLeave;
|
|
248
|
+
const isEndAgent =
|
|
249
|
+
agentConnectionState === AgentState.AGENT_CONNECTED ||
|
|
250
|
+
agentConnectionState === AgentState.AGENT_DISCONNECT_FAILED;
|
|
251
|
+
|
|
252
|
+
const backgroundColorStyle = isMobileUA()
|
|
253
|
+
? {backgroundColor: isEndAgent ? '#FF414D' : '#00C2FF', height: 72}
|
|
254
|
+
: {};
|
|
255
|
+
const fontcolorStyle = isMobileUA()
|
|
256
|
+
? {color: '#FFF'}
|
|
257
|
+
: {color: isEndAgent ? '#FF414D' : '#00C2FF'};
|
|
258
|
+
return (
|
|
259
|
+
<TouchableOpacity
|
|
260
|
+
style={{
|
|
261
|
+
display: 'flex',
|
|
262
|
+
height: 48,
|
|
263
|
+
padding: 20,
|
|
264
|
+
justifyContent: 'space-between',
|
|
265
|
+
alignItems: 'center',
|
|
266
|
+
gap: 8,
|
|
267
|
+
borderRadius: 40,
|
|
268
|
+
borderWidth: isMobileUA() ? 0 : 1,
|
|
269
|
+
borderColor: isEndAgent ? '#FF414D' : '#00C2FF',
|
|
270
|
+
flexDirection: 'row',
|
|
271
|
+
...backgroundColorStyle,
|
|
272
|
+
}}
|
|
273
|
+
onPress={handleConnectionToggle}
|
|
274
|
+
disabled={isLoading}>
|
|
275
|
+
{isLoading ? (
|
|
276
|
+
<ActivityIndicator
|
|
277
|
+
size="small"
|
|
278
|
+
color={isMobileUA() ? '#FFFFFF' : '#00C2FF'}
|
|
279
|
+
/>
|
|
280
|
+
) : isStartAgent ? (
|
|
281
|
+
<CallIcon fill={isMobileUA() ? '#FFFFFF' : '#00C2FF'} />
|
|
282
|
+
) : (
|
|
283
|
+
<EndCall fill={isMobileUA() ? '#FFFFFF' : '#FF414D'} />
|
|
284
|
+
)}
|
|
285
|
+
|
|
286
|
+
<Text
|
|
287
|
+
style={{
|
|
288
|
+
fontFamily: ThemeConfig.FontFamily.sansPro,
|
|
289
|
+
...fontcolorStyle,
|
|
290
|
+
}}>{`${AI_AGENT_STATE[agentConnectionState]}`}</Text>
|
|
291
|
+
</TouchableOpacity>
|
|
292
|
+
);
|
|
293
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {Text, View} from 'react-native';
|
|
3
|
+
import {LiveAudioVisualizer} from './react-audio-visualize';
|
|
4
|
+
import {DisconnectedIconDesktop, DisconnectedIconMobile} from './icons';
|
|
5
|
+
import {isMobileUA} from '../../utils/common';
|
|
6
|
+
|
|
7
|
+
export const DisconnectedView = ({isConnected}) => {
|
|
8
|
+
return (
|
|
9
|
+
<View
|
|
10
|
+
style={{
|
|
11
|
+
flex: 1,
|
|
12
|
+
backgroundColor: '#222',
|
|
13
|
+
display: 'flex',
|
|
14
|
+
alignItems: 'center',
|
|
15
|
+
justifyContent: 'center',
|
|
16
|
+
}}>
|
|
17
|
+
{/* big circle that covers the parent view */}
|
|
18
|
+
{isMobileUA() ? <DisconnectedIconMobile /> : <DisconnectedIconDesktop />}
|
|
19
|
+
<Text
|
|
20
|
+
style={{
|
|
21
|
+
color: '#B3B3B3',
|
|
22
|
+
fontSize: 20,
|
|
23
|
+
fontWeight: '400',
|
|
24
|
+
marginTop: 20,
|
|
25
|
+
}}>
|
|
26
|
+
{isConnected ? '' : 'Not Joined'}
|
|
27
|
+
</Text>
|
|
28
|
+
</View>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function createSilentAudioTrack(): MediaStreamTrack {
|
|
33
|
+
const audioContext = new (window.AudioContext ||
|
|
34
|
+
(window as any).webkitAudioContext)();
|
|
35
|
+
const oscillator = audioContext.createOscillator();
|
|
36
|
+
const dst = audioContext.createMediaStreamDestination();
|
|
37
|
+
oscillator.connect(dst);
|
|
38
|
+
oscillator.start();
|
|
39
|
+
const track = dst.stream.getAudioTracks()[0];
|
|
40
|
+
return Object.assign(track, {enabled: false});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const emptyAudioTrack = {
|
|
44
|
+
getMediaStreamTrack: () => createSilentAudioTrack(),
|
|
45
|
+
getVolumeLevel: () => 0,
|
|
46
|
+
setVolume: () => {},
|
|
47
|
+
setEnabled: () => {},
|
|
48
|
+
play: () => {},
|
|
49
|
+
stop: () => {},
|
|
50
|
+
setPlaybackDevice: () => Promise.resolve(),
|
|
51
|
+
getStats: () => ({
|
|
52
|
+
receiveBytes: 0,
|
|
53
|
+
receivePackets: 0,
|
|
54
|
+
receivePacketsLost: 0,
|
|
55
|
+
state: 'stopped',
|
|
56
|
+
}),
|
|
57
|
+
isPlaying: false,
|
|
58
|
+
processTrack: {
|
|
59
|
+
on: () => {},
|
|
60
|
+
off: () => {},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const AudioVisualizer = ({audioTrack}) => {
|
|
65
|
+
return (
|
|
66
|
+
<View
|
|
67
|
+
style={{
|
|
68
|
+
flex: 1,
|
|
69
|
+
backgroundColor: '#222',
|
|
70
|
+
display: 'flex',
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
justifyContent: 'center',
|
|
73
|
+
}}>
|
|
74
|
+
<LiveAudioVisualizer
|
|
75
|
+
audioTrack={audioTrack || emptyAudioTrack}
|
|
76
|
+
width={300}
|
|
77
|
+
height={400}
|
|
78
|
+
fftSize={32}
|
|
79
|
+
barWidth={10}
|
|
80
|
+
minDecibels={-60}
|
|
81
|
+
maxDecibels={-10}
|
|
82
|
+
gap={2}
|
|
83
|
+
backgroundColor="transparent"
|
|
84
|
+
barColor="#00C2FF"
|
|
85
|
+
smoothingTimeConstant={0.9}
|
|
86
|
+
/>
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default AudioVisualizer;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ToolbarPreset,
|
|
3
|
+
ToolbarComponents,
|
|
4
|
+
useSidePanel,
|
|
5
|
+
useRoomInfo,
|
|
6
|
+
} from 'customization-api';
|
|
7
|
+
import ThemeConfig from '../../theme';
|
|
8
|
+
import {isMobileUA} from '../../utils/common';
|
|
9
|
+
import React, {useEffect, useState} from 'react';
|
|
10
|
+
import {Text, View} from 'react-native';
|
|
11
|
+
import {AgentControl} from './AgentControls';
|
|
12
|
+
import {LogoIcon} from './icons';
|
|
13
|
+
|
|
14
|
+
export const LogoComponent = () => {
|
|
15
|
+
return (
|
|
16
|
+
<View
|
|
17
|
+
style={{
|
|
18
|
+
flexDirection: 'row',
|
|
19
|
+
alignItems: 'center',
|
|
20
|
+
justifyContent: 'center',
|
|
21
|
+
gap: 8,
|
|
22
|
+
marginRight: 20,
|
|
23
|
+
}}>
|
|
24
|
+
<LogoIcon />
|
|
25
|
+
<Text
|
|
26
|
+
style={{
|
|
27
|
+
color: '#C3C3C3',
|
|
28
|
+
textAlign: 'center',
|
|
29
|
+
fontSize: 18,
|
|
30
|
+
fontStyle: 'normal',
|
|
31
|
+
fontWeight: '600',
|
|
32
|
+
lineHeight: 18,
|
|
33
|
+
fontFamily: ThemeConfig.FontFamily.sansPro,
|
|
34
|
+
}}>
|
|
35
|
+
AI Builder Demo
|
|
36
|
+
</Text>
|
|
37
|
+
</View>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const Bottombar = () => {
|
|
42
|
+
const {MeetingTitleToolbarItem, ParticipantCountToolbarItem} =
|
|
43
|
+
ToolbarComponents;
|
|
44
|
+
const {setSidePanel} = useSidePanel();
|
|
45
|
+
const {data} = useRoomInfo();
|
|
46
|
+
const [clientId, setClientId] = useState<string | null>(null);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
!isMobileUA() && setSidePanel('agent-transcript-panel');
|
|
49
|
+
}, []);
|
|
50
|
+
return (
|
|
51
|
+
<ToolbarPreset
|
|
52
|
+
align="bottom"
|
|
53
|
+
items={{
|
|
54
|
+
layout: {hide: true},
|
|
55
|
+
invite: {hide: true},
|
|
56
|
+
more: {hide: true},
|
|
57
|
+
logo: {
|
|
58
|
+
align: 'start',
|
|
59
|
+
order: 0,
|
|
60
|
+
component: () => <LogoComponent />,
|
|
61
|
+
},
|
|
62
|
+
'meeting-title': {
|
|
63
|
+
align: 'start',
|
|
64
|
+
component: MeetingTitleToolbarItem,
|
|
65
|
+
order: 1,
|
|
66
|
+
hide: true,
|
|
67
|
+
},
|
|
68
|
+
'participant-count': {
|
|
69
|
+
align: 'start',
|
|
70
|
+
component: ParticipantCountToolbarItem,
|
|
71
|
+
order: 2,
|
|
72
|
+
hide: true,
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
'connect-agent': {
|
|
76
|
+
align: 'end',
|
|
77
|
+
label: 'Agent',
|
|
78
|
+
component: () => <AgentControl channel_name={data.channel} />,
|
|
79
|
+
order: 3,
|
|
80
|
+
},
|
|
81
|
+
'local-video': {hide: true},
|
|
82
|
+
screenshare: {hide: true},
|
|
83
|
+
recording: {hide: true},
|
|
84
|
+
'local-audio': {align: 'end', order: 1},
|
|
85
|
+
'end-call': {align: 'end', order: 2, hide: true},
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default Bottombar;
|