agora-appbuilder-core 4.1.0-beta-3 → 4.1.0-beta-4
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 +1 -1
- package/template/customization-api/customize.tsx +11 -0
- package/template/src/ai-agent/assets/join-call.png +0 -0
- package/template/src/ai-agent/assets/leave-call.png +0 -0
- package/template/src/ai-agent/components/AgentControls/AgentConnectionWrapper.tsx +248 -0
- package/template/src/ai-agent/components/AgentControls/AgentContext.tsx +7 -0
- package/template/src/ai-agent/components/AgentControls/index.tsx +43 -257
- package/template/src/ai-agent/components/Bottombar.tsx +3 -5
- package/template/src/ai-agent/components/CustomSettingsPanel.tsx +43 -5
- package/template/src/ai-agent/components/SelectAiAgent.tsx +15 -12
- package/template/src/ai-agent/components/UserPrompt.tsx +64 -0
- package/template/src/ai-agent/components/agent-chat-panel/agent-chat-ui.tsx +12 -10
- package/template/src/ai-agent/components/mobile/Bottombar.tsx +55 -39
- package/template/src/ai-agent/components/mobile/Topbar.tsx +1 -2
- package/template/src/ai-agent/components/utils.ts +17 -0
- package/template/src/ai-agent/index.tsx +18 -12
- package/template/src/auth/AuthProvider.tsx +19 -0
- package/template/src/components/room-info/useRoomInfo.tsx +15 -4
- package/template/src/utils/useJoinRoom.ts +34 -8
- package/template/src/ai-agent/components/AgentControls/LeaveCall.png +0 -0
- package/template/src/ai-agent/components/AgentControls/Vector.svg +0 -3
- package/template/src/ai-agent/components/icons.tsx +0 -227
package/package.json
CHANGED
|
@@ -177,6 +177,17 @@ const mergeCustomization = (
|
|
|
177
177
|
);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
if (externalConfig?.components?.videoCall?.wrapper) {
|
|
181
|
+
const AiAgentVideoCallWrapper = aiAgentConfig.components.videoCall.wrapper;
|
|
182
|
+
const ExternalVideoCallWrapper =
|
|
183
|
+
externalConfig.components.videoCall.wrapper;
|
|
184
|
+
mergedData.components.videoCall.wrapper = props => (
|
|
185
|
+
<AiAgentVideoCallWrapper>
|
|
186
|
+
<ExternalVideoCallWrapper>{props.children}</ExternalVideoCallWrapper>
|
|
187
|
+
</AiAgentVideoCallWrapper>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
180
191
|
//override the i18n
|
|
181
192
|
if (externalConfig?.i18n && externalConfig?.i18n?.length) {
|
|
182
193
|
mergedData.i18n = externalConfig.i18n;
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {UidType, useContent, useRoomInfo, Toast} from 'customization-api';
|
|
2
|
+
import React, {createContext, useContext, useEffect} from 'react';
|
|
3
|
+
import {AgentContext} from './AgentContext';
|
|
4
|
+
import {AgentState} from './const';
|
|
5
|
+
import StorageContext from '../../../components/StorageContext';
|
|
6
|
+
|
|
7
|
+
export interface AgentContextInterface {
|
|
8
|
+
toggleAgentConnection: (forceStop?: boolean) => Promise<boolean>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const AgentConnectionContext = createContext<AgentContextInterface>({
|
|
12
|
+
toggleAgentConnection: () => {
|
|
13
|
+
return Promise.resolve(false);
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const AgentConnectionProvider: React.FC<{children: React.ReactNode}> = ({
|
|
18
|
+
children,
|
|
19
|
+
}) => {
|
|
20
|
+
const {activeUids: users} = useContent();
|
|
21
|
+
const {
|
|
22
|
+
agentUID,
|
|
23
|
+
agentConnectionState,
|
|
24
|
+
setAgentConnectionState,
|
|
25
|
+
agentId,
|
|
26
|
+
setAgentUID,
|
|
27
|
+
prompt,
|
|
28
|
+
} = useContext(AgentContext);
|
|
29
|
+
const {
|
|
30
|
+
data: {channel: channel_name, uid: localUid, agents},
|
|
31
|
+
} = useRoomInfo();
|
|
32
|
+
const {store} = useContext(StorageContext);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
console.log('debugging users agent contrl', {users});
|
|
36
|
+
// welcome agent
|
|
37
|
+
const aiAgentUID = users.filter(item => item === agentUID);
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
aiAgentUID.length &&
|
|
41
|
+
agentConnectionState === AgentState.AWAITING_JOIN
|
|
42
|
+
) {
|
|
43
|
+
setAgentConnectionState(AgentState.AGENT_CONNECTED);
|
|
44
|
+
|
|
45
|
+
Toast.show({
|
|
46
|
+
leadingIconName: 'tick-fill',
|
|
47
|
+
type: 'success',
|
|
48
|
+
text1: 'Say Hi!!',
|
|
49
|
+
text2: null,
|
|
50
|
+
visibilityTime: 3000,
|
|
51
|
+
primaryBtn: null,
|
|
52
|
+
secondaryBtn: null,
|
|
53
|
+
leadingIcon: null,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// when agent leaves, show left toast, and set agent to not connected state
|
|
57
|
+
if (
|
|
58
|
+
!aiAgentUID.length &&
|
|
59
|
+
agentConnectionState === AgentState.AWAITING_LEAVE
|
|
60
|
+
) {
|
|
61
|
+
setAgentConnectionState(AgentState.NOT_CONNECTED);
|
|
62
|
+
}
|
|
63
|
+
}, [users, agentUID]);
|
|
64
|
+
|
|
65
|
+
const handleConnectionToggle = async (forceStop: boolean = false) => {
|
|
66
|
+
try {
|
|
67
|
+
// connect to agent when agent is in not connected state or when earlier connect failed
|
|
68
|
+
if (
|
|
69
|
+
agentConnectionState === AgentState.NOT_CONNECTED ||
|
|
70
|
+
agentConnectionState === AgentState.AGENT_REQUEST_FAILED ||
|
|
71
|
+
agentConnectionState === AgentState.AWAITING_LEAVE
|
|
72
|
+
) {
|
|
73
|
+
try {
|
|
74
|
+
setAgentConnectionState(AgentState.REQUEST_SENT);
|
|
75
|
+
const data = await connectToAIAgent(
|
|
76
|
+
'start',
|
|
77
|
+
channel_name,
|
|
78
|
+
localUid,
|
|
79
|
+
store.token,
|
|
80
|
+
{
|
|
81
|
+
agent_id: agentId,
|
|
82
|
+
prompt: prompt,
|
|
83
|
+
voice: agents.find(a => a.id === agentId)?.config?.tts?.params
|
|
84
|
+
?.voice_name,
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
// console.log("response X-Client-ID", newClientId, typeof newClientId)
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
const {agent_uid = null} = data;
|
|
90
|
+
|
|
91
|
+
//setClientId(agent_id);
|
|
92
|
+
setAgentUID(agent_uid);
|
|
93
|
+
|
|
94
|
+
setAgentConnectionState(AgentState.AWAITING_JOIN);
|
|
95
|
+
|
|
96
|
+
Toast.show({
|
|
97
|
+
leadingIconName: 'tick-fill',
|
|
98
|
+
type: 'success',
|
|
99
|
+
text1: 'Agent requested to join',
|
|
100
|
+
text2: null,
|
|
101
|
+
visibilityTime: 3000,
|
|
102
|
+
primaryBtn: null,
|
|
103
|
+
secondaryBtn: null,
|
|
104
|
+
leadingIcon: null,
|
|
105
|
+
});
|
|
106
|
+
return Promise.resolve(true);
|
|
107
|
+
} catch (agentConnectError) {
|
|
108
|
+
setAgentConnectionState(AgentState.AGENT_REQUEST_FAILED);
|
|
109
|
+
|
|
110
|
+
if (agentConnectError.toString().indexOf('401') !== -1) {
|
|
111
|
+
Toast.show({
|
|
112
|
+
leadingIconName: 'alert',
|
|
113
|
+
type: 'error',
|
|
114
|
+
text1: 'Your session is expired. Please sign in to join call.',
|
|
115
|
+
text2: null,
|
|
116
|
+
visibilityTime: 5000,
|
|
117
|
+
primaryBtn: null,
|
|
118
|
+
secondaryBtn: null,
|
|
119
|
+
leadingIcon: null,
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
Toast.show({
|
|
123
|
+
leadingIconName: 'alert',
|
|
124
|
+
type: 'error',
|
|
125
|
+
text1: 'Uh oh! Agent failed to connect',
|
|
126
|
+
text2: null,
|
|
127
|
+
visibilityTime: 5000,
|
|
128
|
+
primaryBtn: null,
|
|
129
|
+
secondaryBtn: null,
|
|
130
|
+
leadingIcon: null,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw agentConnectError;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// disconnect agent with agent is already connected or when earlier disconnect failed
|
|
138
|
+
if (
|
|
139
|
+
forceStop === true ||
|
|
140
|
+
agentConnectionState === AgentState.AGENT_CONNECTED ||
|
|
141
|
+
agentConnectionState === AgentState.AGENT_DISCONNECT_FAILED
|
|
142
|
+
) {
|
|
143
|
+
try {
|
|
144
|
+
setAgentConnectionState(AgentState.AGENT_DISCONNECT_REQUEST);
|
|
145
|
+
await connectToAIAgent('stop', channel_name, localUid, store.token, {
|
|
146
|
+
agent_id: agentId,
|
|
147
|
+
});
|
|
148
|
+
setAgentConnectionState(AgentState.AWAITING_LEAVE);
|
|
149
|
+
if (!forceStop) {
|
|
150
|
+
Toast.show({
|
|
151
|
+
leadingIconName: 'tick-fill',
|
|
152
|
+
type: 'success',
|
|
153
|
+
text1: 'Agent disconnected',
|
|
154
|
+
text2: null,
|
|
155
|
+
visibilityTime: 3000,
|
|
156
|
+
primaryBtn: null,
|
|
157
|
+
secondaryBtn: null,
|
|
158
|
+
leadingIcon: null,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return Promise.resolve(true);
|
|
162
|
+
} catch (agentDisconnectError) {
|
|
163
|
+
setAgentConnectionState(AgentState.AGENT_DISCONNECT_FAILED);
|
|
164
|
+
|
|
165
|
+
Toast.show({
|
|
166
|
+
leadingIconName: 'alert',
|
|
167
|
+
type: 'error',
|
|
168
|
+
text1: 'Uh oh! Agent failed to disconnect',
|
|
169
|
+
text2: null,
|
|
170
|
+
visibilityTime: 5000,
|
|
171
|
+
primaryBtn: null,
|
|
172
|
+
secondaryBtn: null,
|
|
173
|
+
leadingIcon: null,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
throw agentDisconnectError;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log(`Agent failed to connect/disconnect - ${error}`);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const value = {
|
|
185
|
+
toggleAgentConnection: handleConnectionToggle,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<AgentConnectionContext.Provider value={value}>
|
|
190
|
+
{children}
|
|
191
|
+
</AgentConnectionContext.Provider>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const connectToAIAgent = async (
|
|
196
|
+
agentAction: 'start' | 'stop',
|
|
197
|
+
channel_name: string,
|
|
198
|
+
localUid: UidType,
|
|
199
|
+
agentAuthToken: string,
|
|
200
|
+
data?: {agent_id: string; prompt?: string; voice?: string},
|
|
201
|
+
): Promise<{}> => {
|
|
202
|
+
// const apiUrl = '/api/proxy';
|
|
203
|
+
const apiUrl = $config.BACKEND_ENDPOINT + '/v1/convoai';
|
|
204
|
+
const requestBody = {
|
|
205
|
+
channel_name: channel_name,
|
|
206
|
+
uid: localUid, // user uid // localUid or 0
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
if (data && data?.agent_id) {
|
|
210
|
+
requestBody['ai_agent_id'] = data.agent_id;
|
|
211
|
+
}
|
|
212
|
+
if (data && data?.voice) {
|
|
213
|
+
requestBody['voice'] = data.voice;
|
|
214
|
+
}
|
|
215
|
+
if (data && data?.prompt) {
|
|
216
|
+
requestBody['prompt'] = data.prompt;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const headers: HeadersInit = {
|
|
220
|
+
'Content-Type': 'application/json',
|
|
221
|
+
Authorization: `Bearer ${agentAuthToken}`,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const response = await fetch(`${apiUrl}/${agentAction}`, {
|
|
226
|
+
method: 'POST',
|
|
227
|
+
headers: headers,
|
|
228
|
+
body: JSON.stringify(requestBody),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const data = await response.json();
|
|
236
|
+
|
|
237
|
+
console.log(
|
|
238
|
+
`AI agent ${agentAction === 'start' ? 'connected' : 'disconnected'}`,
|
|
239
|
+
data,
|
|
240
|
+
);
|
|
241
|
+
if (agentAction === 'start') {
|
|
242
|
+
return data;
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(`Failed to ${agentAction} AI agent connection:`, error);
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
@@ -26,6 +26,8 @@ export interface AgentContextInterface {
|
|
|
26
26
|
setAgentId: (id: string) => void;
|
|
27
27
|
agentVoice?: keyof typeof AI_AGENT_VOICE | '';
|
|
28
28
|
setAgentVoice: (voice: keyof typeof AI_AGENT_VOICE) => void;
|
|
29
|
+
prompt?: string;
|
|
30
|
+
setPrompt: (prompt: string) => void;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export const AgentContext = createContext<AgentContextInterface>({
|
|
@@ -43,6 +45,8 @@ export const AgentContext = createContext<AgentContextInterface>({
|
|
|
43
45
|
setAgentVoice: () => {},
|
|
44
46
|
agentId: '',
|
|
45
47
|
setAgentId: () => {},
|
|
48
|
+
prompt: '',
|
|
49
|
+
setPrompt: () => {},
|
|
46
50
|
});
|
|
47
51
|
|
|
48
52
|
/**
|
|
@@ -85,6 +89,7 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
85
89
|
const [agentId, setAgentId] = useState('');
|
|
86
90
|
const [agentVoice, setAgentVoice] =
|
|
87
91
|
useState<AgentContextInterface['agentVoice']>('');
|
|
92
|
+
const [prompt, setPrompt] = useState('');
|
|
88
93
|
|
|
89
94
|
/**
|
|
90
95
|
* Adds a new chat item to the chat state while ensuring:
|
|
@@ -170,6 +175,8 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
170
175
|
setAgentId,
|
|
171
176
|
agentVoice,
|
|
172
177
|
setAgentVoice,
|
|
178
|
+
prompt,
|
|
179
|
+
setPrompt,
|
|
173
180
|
};
|
|
174
181
|
|
|
175
182
|
return (
|
|
@@ -1,95 +1,22 @@
|
|
|
1
|
-
import React, {useContext
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
2
|
import {AI_AGENT_STATE, AgentState} from './const';
|
|
3
|
-
import {TouchableOpacity, Text, ActivityIndicator} from 'react-native';
|
|
3
|
+
import {TouchableOpacity, Text, ActivityIndicator, Image} from 'react-native';
|
|
4
4
|
import {AgentContext} from './AgentContext';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
useStorageContext,
|
|
12
|
-
Toast,
|
|
13
|
-
} from 'customization-api';
|
|
5
|
+
import {ThemeConfig} from 'customization-api';
|
|
6
|
+
//@ts-ignore
|
|
7
|
+
import JoinCallIcon from '../../assets/join-call.png';
|
|
8
|
+
//@ts-ignore
|
|
9
|
+
import LeaveCallIcon from '../../assets/leave-call.png';
|
|
10
|
+
import {AgentConnectionContext} from './AgentConnectionWrapper';
|
|
14
11
|
import {isMobileUA} from '../../../utils/common';
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
const connectToAIAgent = async (
|
|
18
|
-
agentAction: 'start' | 'stop',
|
|
19
|
-
channel_name: string,
|
|
20
|
-
localUid: UidType,
|
|
21
|
-
agentAuthToken: string,
|
|
22
|
-
data?: {agent_id: string; agent_voice: 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
|
-
|
|
31
|
-
if (data && data.agent_id) {
|
|
32
|
-
requestBody['ai_agent_id'] = data.agent_id;
|
|
33
|
-
}
|
|
34
|
-
if (data && data.agent_voice) {
|
|
35
|
-
requestBody['voice'] = data.agent_voice;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
console.log({requestBody});
|
|
39
|
-
const headers: HeadersInit = {
|
|
40
|
-
'Content-Type': 'application/json',
|
|
41
|
-
Authorization: `Bearer ${agentAuthToken}`,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
const response = await fetch(`${apiUrl}/${agentAction}`, {
|
|
46
|
-
method: 'POST',
|
|
47
|
-
headers: headers,
|
|
48
|
-
body: JSON.stringify(requestBody),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const data = await response.json();
|
|
56
|
-
|
|
57
|
-
// console.log({data}, "X-Client-ID start stop")
|
|
58
|
-
console.log(
|
|
59
|
-
`AI agent ${agentAction === 'start' ? 'connected' : 'disconnected'}`,
|
|
60
|
-
data,
|
|
61
|
-
);
|
|
62
|
-
if (agentAction === 'start') {
|
|
63
|
-
return data;
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error(`Failed to ${agentAction} AI agent connection:`, error);
|
|
67
|
-
throw error;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export const AgentControl: React.FC<{channel_name: string}> = ({
|
|
72
|
-
channel_name,
|
|
73
|
-
}) => {
|
|
74
|
-
const {
|
|
75
|
-
agentConnectionState,
|
|
76
|
-
setAgentConnectionState,
|
|
77
|
-
agentAuthToken,
|
|
78
|
-
setAgentAuthToken,
|
|
79
|
-
agentUID,
|
|
80
|
-
setAgentUID,
|
|
81
|
-
agentVoice,
|
|
82
|
-
agentId,
|
|
83
|
-
} = useContext(AgentContext);
|
|
84
|
-
// console.log("X-Client-ID state", clientId)
|
|
85
|
-
// const { users } = useContext(UserContext)
|
|
86
|
-
const {activeUids: users} = useContent();
|
|
87
|
-
const endcall = useEndCall();
|
|
88
|
-
const {store} = useStorageContext();
|
|
89
|
-
const localUid = useLocalUid();
|
|
12
|
+
import {useIsAgentAvailable} from '../utils';
|
|
90
13
|
|
|
14
|
+
export const AgentControl: React.FC = () => {
|
|
15
|
+
const {agentConnectionState} = useContext(AgentContext);
|
|
16
|
+
const {toggleAgentConnection} = useContext(AgentConnectionContext);
|
|
91
17
|
// stop_agent API is successful, but agent has not yet left the RTC channel
|
|
92
18
|
const isAwaitingLeave = agentConnectionState === AgentState.AWAITING_LEAVE;
|
|
19
|
+
const isAgentAvailable = useIsAgentAvailable();
|
|
93
20
|
|
|
94
21
|
console.log(
|
|
95
22
|
'Agent Control--',
|
|
@@ -97,161 +24,6 @@ export const AgentControl: React.FC<{channel_name: string}> = ({
|
|
|
97
24
|
{bth: AI_AGENT_STATE[agentConnectionState]},
|
|
98
25
|
);
|
|
99
26
|
|
|
100
|
-
const handleConnectionToggle = async () => {
|
|
101
|
-
try {
|
|
102
|
-
// connect to agent when agent is in not connected state or when earlier connect failed
|
|
103
|
-
if (
|
|
104
|
-
agentConnectionState === AgentState.NOT_CONNECTED ||
|
|
105
|
-
agentConnectionState === AgentState.AGENT_REQUEST_FAILED ||
|
|
106
|
-
isAwaitingLeave
|
|
107
|
-
) {
|
|
108
|
-
try {
|
|
109
|
-
setAgentConnectionState(AgentState.REQUEST_SENT);
|
|
110
|
-
const data = await connectToAIAgent(
|
|
111
|
-
'start',
|
|
112
|
-
channel_name,
|
|
113
|
-
localUid,
|
|
114
|
-
store.token,
|
|
115
|
-
{
|
|
116
|
-
agent_id: agentId,
|
|
117
|
-
agent_voice: agentVoice,
|
|
118
|
-
},
|
|
119
|
-
);
|
|
120
|
-
// console.log("response X-Client-ID", newClientId, typeof newClientId)
|
|
121
|
-
// @ts-ignore
|
|
122
|
-
const {agent_uid = null} = data;
|
|
123
|
-
|
|
124
|
-
//setClientId(agent_id);
|
|
125
|
-
setAgentUID(agent_uid);
|
|
126
|
-
|
|
127
|
-
setAgentConnectionState(AgentState.AWAITING_JOIN);
|
|
128
|
-
|
|
129
|
-
Toast.show({
|
|
130
|
-
leadingIconName: 'tick-fill',
|
|
131
|
-
type: 'success',
|
|
132
|
-
text1: 'Agent requested to join',
|
|
133
|
-
text2: null,
|
|
134
|
-
visibilityTime: 3000,
|
|
135
|
-
primaryBtn: null,
|
|
136
|
-
secondaryBtn: null,
|
|
137
|
-
leadingIcon: null,
|
|
138
|
-
});
|
|
139
|
-
} catch (agentConnectError) {
|
|
140
|
-
setAgentConnectionState(AgentState.AGENT_REQUEST_FAILED);
|
|
141
|
-
|
|
142
|
-
if (agentConnectError.toString().indexOf('401') !== -1) {
|
|
143
|
-
Toast.show({
|
|
144
|
-
leadingIconName: 'alert',
|
|
145
|
-
type: 'error',
|
|
146
|
-
text1: 'Your session is expired. Please sign in to join call.',
|
|
147
|
-
text2: null,
|
|
148
|
-
visibilityTime: 5000,
|
|
149
|
-
primaryBtn: null,
|
|
150
|
-
secondaryBtn: null,
|
|
151
|
-
leadingIcon: null,
|
|
152
|
-
});
|
|
153
|
-
// window.location.href = '/create'
|
|
154
|
-
await endcall();
|
|
155
|
-
setAgentAuthToken(null);
|
|
156
|
-
return;
|
|
157
|
-
} else {
|
|
158
|
-
Toast.show({
|
|
159
|
-
leadingIconName: 'alert',
|
|
160
|
-
type: 'error',
|
|
161
|
-
text1: 'Uh oh! Agent failed to connect',
|
|
162
|
-
text2: null,
|
|
163
|
-
visibilityTime: 5000,
|
|
164
|
-
primaryBtn: null,
|
|
165
|
-
secondaryBtn: null,
|
|
166
|
-
leadingIcon: null,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
throw agentConnectError;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// disconnect agent with agent is already connected or when earlier disconnect failed
|
|
174
|
-
if (
|
|
175
|
-
agentConnectionState === AgentState.AGENT_CONNECTED ||
|
|
176
|
-
agentConnectionState === AgentState.AGENT_DISCONNECT_FAILED
|
|
177
|
-
) {
|
|
178
|
-
if (isMobileUA()) {
|
|
179
|
-
await endcall();
|
|
180
|
-
setAgentConnectionState(AgentState.NOT_CONNECTED);
|
|
181
|
-
setAgentAuthToken(null);
|
|
182
|
-
return; // check later
|
|
183
|
-
}
|
|
184
|
-
try {
|
|
185
|
-
setAgentConnectionState(AgentState.AGENT_DISCONNECT_REQUEST);
|
|
186
|
-
await connectToAIAgent('stop', channel_name, localUid, store.token, {
|
|
187
|
-
agent_id: agentId,
|
|
188
|
-
agent_voice: agentVoice,
|
|
189
|
-
});
|
|
190
|
-
setAgentConnectionState(AgentState.AWAITING_LEAVE);
|
|
191
|
-
|
|
192
|
-
Toast.show({
|
|
193
|
-
leadingIconName: 'tick-fill',
|
|
194
|
-
type: 'success',
|
|
195
|
-
text1: 'Agent disconnected',
|
|
196
|
-
text2: null,
|
|
197
|
-
visibilityTime: 3000,
|
|
198
|
-
primaryBtn: null,
|
|
199
|
-
secondaryBtn: null,
|
|
200
|
-
leadingIcon: null,
|
|
201
|
-
});
|
|
202
|
-
} catch (agentDisconnectError) {
|
|
203
|
-
setAgentConnectionState(AgentState.AGENT_DISCONNECT_FAILED);
|
|
204
|
-
|
|
205
|
-
Toast.show({
|
|
206
|
-
leadingIconName: 'alert',
|
|
207
|
-
type: 'error',
|
|
208
|
-
text1: 'Uh oh! Agent failed to disconnect',
|
|
209
|
-
text2: null,
|
|
210
|
-
visibilityTime: 5000,
|
|
211
|
-
primaryBtn: null,
|
|
212
|
-
secondaryBtn: null,
|
|
213
|
-
leadingIcon: null,
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
throw agentDisconnectError;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
} catch (error) {
|
|
220
|
-
console.log(`Agent failed to connect/disconnect - ${error}`);
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
useEffect(() => {
|
|
225
|
-
console.log('agent contrl', {users});
|
|
226
|
-
// welcome agent
|
|
227
|
-
const aiAgentUID = users.filter(item => item === agentUID);
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
aiAgentUID.length &&
|
|
231
|
-
agentConnectionState === AgentState.AWAITING_JOIN
|
|
232
|
-
) {
|
|
233
|
-
setAgentConnectionState(AgentState.AGENT_CONNECTED);
|
|
234
|
-
|
|
235
|
-
Toast.show({
|
|
236
|
-
leadingIconName: 'tick-fill',
|
|
237
|
-
type: 'success',
|
|
238
|
-
text1: 'Say Hi!!',
|
|
239
|
-
text2: null,
|
|
240
|
-
visibilityTime: 3000,
|
|
241
|
-
primaryBtn: null,
|
|
242
|
-
secondaryBtn: null,
|
|
243
|
-
leadingIcon: null,
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
// when agent leaves, show left toast, and set agent to not connected state
|
|
247
|
-
if (
|
|
248
|
-
!aiAgentUID.length &&
|
|
249
|
-
agentConnectionState === AgentState.AWAITING_LEAVE
|
|
250
|
-
) {
|
|
251
|
-
setAgentConnectionState(AgentState.NOT_CONNECTED);
|
|
252
|
-
}
|
|
253
|
-
}, [users]);
|
|
254
|
-
|
|
255
27
|
const isLoading =
|
|
256
28
|
agentConnectionState === AgentState.REQUEST_SENT ||
|
|
257
29
|
agentConnectionState === AgentState.AGENT_DISCONNECT_REQUEST ||
|
|
@@ -273,27 +45,41 @@ export const AgentControl: React.FC<{channel_name: string}> = ({
|
|
|
273
45
|
|
|
274
46
|
return (
|
|
275
47
|
<TouchableOpacity
|
|
276
|
-
style={
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
48
|
+
style={[
|
|
49
|
+
isMobileUA()
|
|
50
|
+
? {
|
|
51
|
+
display: 'flex',
|
|
52
|
+
height: 48,
|
|
53
|
+
width: 48,
|
|
54
|
+
marginHorizontal: 24,
|
|
55
|
+
justifyContent: 'center',
|
|
56
|
+
alignItems: 'center',
|
|
57
|
+
borderRadius: 40,
|
|
58
|
+
flexDirection: 'row',
|
|
59
|
+
...backgroundColorStyle,
|
|
60
|
+
}
|
|
61
|
+
: {
|
|
62
|
+
display: 'flex',
|
|
63
|
+
height: 48,
|
|
64
|
+
padding: 20,
|
|
65
|
+
justifyContent: 'space-between',
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
borderRadius: 40,
|
|
68
|
+
flexDirection: 'row',
|
|
69
|
+
...backgroundColorStyle,
|
|
70
|
+
},
|
|
71
|
+
{opacity: !isAgentAvailable ? 0.6 : 1},
|
|
72
|
+
]}
|
|
73
|
+
onPress={() => toggleAgentConnection()}
|
|
74
|
+
disabled={isLoading || !isAgentAvailable}>
|
|
289
75
|
{isLoading ? (
|
|
290
76
|
<ActivityIndicator size="small" color={$config.FONT_COLOR} />
|
|
291
77
|
) : isStartAgent ? (
|
|
292
|
-
<
|
|
78
|
+
<Image style={{width: 24, height: 24}} source={JoinCallIcon} />
|
|
293
79
|
) : (
|
|
294
|
-
<
|
|
80
|
+
<Image style={{width: 24, height: 24}} source={LeaveCallIcon} />
|
|
295
81
|
)}
|
|
296
|
-
{!(agentConnectionState === 'AGENT_CONNECTED') ? (
|
|
82
|
+
{!isMobileUA() && !(agentConnectionState === 'AGENT_CONNECTED') ? (
|
|
297
83
|
<Text
|
|
298
84
|
style={{
|
|
299
85
|
fontFamily: ThemeConfig.FontFamily.sansPro,
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
ToolbarPreset,
|
|
3
3
|
ToolbarComponents,
|
|
4
4
|
useSidePanel,
|
|
5
|
-
useRoomInfo,
|
|
6
5
|
IconButton,
|
|
7
6
|
SidePanelType,
|
|
8
7
|
useActionSheet,
|
|
@@ -13,7 +12,7 @@ import {isMobileUA} from '../../utils/common';
|
|
|
13
12
|
import React, {useEffect} from 'react';
|
|
14
13
|
import {AgentControl} from './AgentControls';
|
|
15
14
|
|
|
16
|
-
const CustomSettingButton = () => {
|
|
15
|
+
export const CustomSettingButton = () => {
|
|
17
16
|
const {sidePanel, setSidePanel} = useSidePanel();
|
|
18
17
|
|
|
19
18
|
const isPanelActive = sidePanel === 'custom-settings-panel';
|
|
@@ -59,7 +58,7 @@ const CustomSettingButton = () => {
|
|
|
59
58
|
);
|
|
60
59
|
};
|
|
61
60
|
|
|
62
|
-
const CustomTranscriptButton = () => {
|
|
61
|
+
export const CustomTranscriptButton = () => {
|
|
63
62
|
const {sidePanel, setSidePanel} = useSidePanel();
|
|
64
63
|
|
|
65
64
|
const isPanelActive = sidePanel === 'agent-transcript-panel';
|
|
@@ -107,7 +106,6 @@ const CustomTranscriptButton = () => {
|
|
|
107
106
|
const Bottombar = () => {
|
|
108
107
|
const {MeetingTitleToolbarItem} = ToolbarComponents;
|
|
109
108
|
const {setSidePanel} = useSidePanel();
|
|
110
|
-
const {data} = useRoomInfo();
|
|
111
109
|
useEffect(() => {
|
|
112
110
|
!isMobileUA() && setSidePanel('custom-settings-panel');
|
|
113
111
|
}, []);
|
|
@@ -134,7 +132,7 @@ const Bottombar = () => {
|
|
|
134
132
|
'connect-agent': {
|
|
135
133
|
align: 'center',
|
|
136
134
|
label: 'Agent',
|
|
137
|
-
component:
|
|
135
|
+
component: AgentControl,
|
|
138
136
|
order: 2,
|
|
139
137
|
},
|
|
140
138
|
'end-call': {align: 'center', order: 3, hide: true},
|