agora-appbuilder-core 4.1.5 → 4.1.6-1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.1.5",
3
+ "version": "4.1.6-1",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -797,8 +797,11 @@ export default class RtcEngine {
797
797
  mediaType,
798
798
  );
799
799
  if (mediaType === 'audio') {
800
- const {audio, ...rest} = this.remoteStreams.get(user.uid);
801
- this.remoteStreams.set(user.uid, rest);
800
+ const data = this.remoteStreams.get(user.uid);
801
+ try {
802
+ delete data['audio'];
803
+ } catch (error) {}
804
+ this.remoteStreams.set(user.uid, {...data});
802
805
  (this.eventsMap.get('onRemoteAudioStateChanged') as callbackType)(
803
806
  {},
804
807
  user.uid,
@@ -807,8 +810,11 @@ export default class RtcEngine {
807
810
  0,
808
811
  );
809
812
  } else {
810
- const {video, ...rest} = this.remoteStreams.get(user.uid);
811
- this.remoteStreams.set(user.uid, rest);
813
+ const data = this.remoteStreams.get(user.uid);
814
+ try {
815
+ delete data['video'];
816
+ } catch (error) {}
817
+ this.remoteStreams.set(user.uid, {...data});
812
818
  (this.eventsMap.get('onRemoteVideoStateChanged') as callbackType)(
813
819
  {},
814
820
  user.uid,
@@ -77,8 +77,8 @@ const DefaultConfig = {
77
77
  CHAT_ORG_NAME: '',
78
78
  CHAT_APP_NAME: '',
79
79
  CHAT_URL: '',
80
- CLI_VERSION: '3.1.5',
81
- CORE_VERSION: '4.1.5',
80
+ CLI_VERSION: '3.1.6-1',
81
+ CORE_VERSION: '4.1.6-1',
82
82
  DISABLE_LANDSCAPE_MODE: false,
83
83
  STT_AUTO_START: false,
84
84
  CLOUD_RECORDING_AUTO_START: false,
@@ -65,7 +65,7 @@
65
65
  "agora-extension-beauty-effect": "^1.0.2-beta",
66
66
  "agora-extension-virtual-background": "^1.1.3",
67
67
  "agora-react-native-rtm": "1.5.1",
68
- "agora-rtc-sdk-ng": "4.23.2",
68
+ "agora-rtc-sdk-ng": "4.23.3",
69
69
  "agora-rtm-sdk": "1.5.1",
70
70
  "buffer": "^6.0.3",
71
71
  "electron-log": "4.3.5",
@@ -4,8 +4,9 @@ import {
4
4
  useContent,
5
5
  useLocalAudio,
6
6
  } from 'customization-api';
7
- import {View, StyleSheet, Platform} from 'react-native';
8
- import hark from 'hark';
7
+ import {View, StyleSheet, Platform, Text} from 'react-native';
8
+ import ThemeConfig from '../../theme';
9
+ import {isAndroid, isIOS, isMobileUA} from '../../utils/common';
9
10
  import {AgentContext} from '../components/AgentControls/AgentContext';
10
11
 
11
12
  const NotJoinedMp4 = require('./1.Not-Joined.mp4').default;
@@ -22,6 +23,17 @@ const AI_ANIMATION_VIDEO = {
22
23
  DISCONNECTING: DisconnectingMp4,
23
24
  };
24
25
 
26
+ const cssHideVideoControls = `
27
+ video {
28
+ -webkit-appearance: none; /* Remove default styling */
29
+ }
30
+ video::-webkit-media-controls {
31
+ display: none !important; /* Hide the controls */
32
+ }
33
+ video::-webkit-media-controls-enclosure {
34
+ display:none !important;
35
+ }`;
36
+
25
37
  export default function AiAgentCustomView({
26
38
  connectionState,
27
39
  }: CustomAgentInterfaceProps) {
@@ -34,27 +46,27 @@ export default function AiAgentCustomView({
34
46
  const aiMediaStream = useRef(null);
35
47
 
36
48
  useEffect(() => {
37
- if (activeUids?.indexOf(agentUID) !== -1 && !aiMediaStream.current) {
49
+ if (
50
+ activeUids?.indexOf(agentUID) !== -1 &&
51
+ !aiMediaStream?.current &&
52
+ !(isAndroid() || isIOS())
53
+ ) {
38
54
  const track = getRemoteAudioStream(agentUID);
39
55
  if (track) {
56
+ const hark = require('hark');
40
57
  try {
41
- if (!aiMediaStream.current) {
42
- console.log('debugging creating MediaStream here');
58
+ if (!aiMediaStream?.current) {
43
59
  aiMediaStream.current = new MediaStream([
44
60
  track.getMediaStreamTrack(),
45
61
  ]);
46
- } else {
47
- console.log('debugging existing MediaStream used');
48
62
  }
49
- const harkai = hark(aiMediaStream.current, {
63
+ const harkai = hark(aiMediaStream?.current, {
50
64
  interval: 100,
51
65
  });
52
66
  harkai.on('speaking', () => {
53
- console.log('debugging ai is speaking');
54
67
  setAISpeaking(true);
55
68
  });
56
69
  harkai.on('stopped_speaking', () => {
57
- console.log('debugging ai is not speaking');
58
70
  setAISpeaking(false);
59
71
  });
60
72
  } catch (error) {
@@ -93,19 +105,58 @@ export default function AiAgentCustomView({
93
105
  }, [connectionState, animation, isAISpeaking]);
94
106
  return (
95
107
  <View style={styles.container}>
96
- <video
97
- autoPlay
98
- style={{pointerEvents: 'none'}}
99
- loop
100
- src={AI_ANIMATION_VIDEO[animation]}
101
- width="40%"
102
- height="40%"
103
- />
108
+ {isAndroid() || isIOS() ? (
109
+ <View style={styles.nativeTextContainer}>
110
+ <Text style={styles.nativeText}>AI Agent...</Text>
111
+ </View>
112
+ ) : (
113
+ <div style={styles.videoContainer}>
114
+ <style type="text/css">{cssHideVideoControls}</style>
115
+ <video
116
+ disablePictureInPicture
117
+ playsInline
118
+ id="animation-video"
119
+ autoPlay
120
+ style={{pointerEvents: 'none'}}
121
+ loop
122
+ src={AI_ANIMATION_VIDEO[animation]}
123
+ width={isMobileUA() ? '40%' : 'auto'}
124
+ height={'auto'}
125
+ />
126
+ <div style={styles.overlay} />
127
+ </div>
128
+ )}
104
129
  </View>
105
130
  );
106
131
  }
107
132
 
108
133
  const styles = StyleSheet.create({
134
+ videoContainer: {
135
+ flex: 1,
136
+ position: 'relative',
137
+ display: 'flex',
138
+ alignItems: 'center',
139
+ justifyContent: 'center',
140
+ },
141
+ overlay: {
142
+ position: 'absolute',
143
+ top: 0,
144
+ left: 0,
145
+ width: '100%',
146
+ height: '100%',
147
+ zIndex: 1,
148
+ },
149
+ nativeTextContainer: {
150
+ flex: 1,
151
+ justifyContent: 'center',
152
+ alignItems: 'center',
153
+ },
154
+ nativeText: {
155
+ fontFamily: ThemeConfig.FontFamily.sansPro,
156
+ fontSize: 18,
157
+ fontWeight: '800',
158
+ color: $config.FONT_COLOR,
159
+ },
109
160
  container: {
110
161
  flex: 1,
111
162
  backgroundColor: 'black',
@@ -330,6 +330,18 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
330
330
  secondaryBtn: null,
331
331
  leadingIcon: null,
332
332
  });
333
+ } else if (agentConnectError.toString().indexOf('403') !== -1) {
334
+ Toast.show({
335
+ leadingIconName: 'alert',
336
+ type: 'error',
337
+ text1: 'Uh oh! Agent failed to connect',
338
+ text2:
339
+ "Verify if you've enabled conversational AI on agora console or if they have configured correct customerId & customer certificate for your project on appbuilder console",
340
+ visibilityTime: 5000,
341
+ primaryBtn: null,
342
+ secondaryBtn: null,
343
+ leadingIcon: null,
344
+ });
333
345
  } else {
334
346
  Toast.show({
335
347
  leadingIconName: 'alert',
@@ -17,7 +17,7 @@ import TranscriptIcon from '../assets/transcript.png';
17
17
  //@ts-ignore
18
18
  import SettingsIcon from '../assets/settings.png';
19
19
  //@ts-ignore
20
- import DisconnectIcon from '../assets/close.png';
20
+ import LeaveCallIcon from '../assets/leave-call.png';
21
21
 
22
22
  export const MicButton = () => {
23
23
  const {audio} = useLocalUserInfo();
@@ -117,7 +117,7 @@ export const DisconnectButton = () => {
117
117
  }}>
118
118
  <Image
119
119
  style={[styles.iconStyle, {tintColor: $config.FONT_COLOR}]}
120
- source={DisconnectIcon}
120
+ source={LeaveCallIcon}
121
121
  />
122
122
  </TouchableOpacity>
123
123
  );
@@ -35,7 +35,10 @@ const SelectAiAgent = () => {
35
35
  }, [agents]);
36
36
 
37
37
  useEffect(() => {
38
- if (!agentId && data && data.length) {
38
+ const storedAgentValidAndActive =
39
+ agentId && agents.find(item => item.id === agentId)?.is_active;
40
+
41
+ if (!storedAgentValidAndActive && data && data.length) {
39
42
  //set default agent
40
43
  setAgentId(data[0]?.value);
41
44
  setStore(prevState => {
@@ -2,13 +2,20 @@ import React from 'react';
2
2
  import {ToolbarPreset} from 'customization-api';
3
3
  import {AgentControl} from '../AgentControls';
4
4
  import {CustomSettingButton, CustomTranscriptButton} from '../Bottombar';
5
+ import {isAndroid, isIOS} from '../../../utils/common';
5
6
 
6
7
  const Bottombar = () => {
7
8
  const AI_LAYOUT = $config.AI_LAYOUT ? $config.AI_LAYOUT : 'LAYOUT_TYPE_1';
8
9
  return (
9
10
  <ToolbarPreset
10
11
  align="bottom"
11
- snapPointsMinMax={AI_LAYOUT === 'LAYOUT_TYPE_1' ? [100, 100] : [0, 0]}
12
+ snapPointsMinMax={
13
+ isAndroid() || isIOS()
14
+ ? [100, 100]
15
+ : AI_LAYOUT === 'LAYOUT_TYPE_2'
16
+ ? [100, 100]
17
+ : [0, 0]
18
+ }
12
19
  items={{
13
20
  layout: {hide: true},
14
21
  invite: {hide: true},
@@ -2,16 +2,26 @@ import React from 'react';
2
2
  import {View, StyleSheet, Text} from 'react-native';
3
3
  import {useRoomInfo} from 'customization-api';
4
4
  import ThemeConfig from '../../../theme';
5
+ import {SettingButton} from '../ControlButtons';
6
+ import {getAILayoutType} from '../../utils';
5
7
 
6
8
  const MobileTopbar = () => {
7
9
  const {
8
10
  data: {meetingTitle},
9
11
  } = useRoomInfo();
12
+
10
13
  return (
11
14
  <View style={style.rootStyle}>
12
15
  <View style={style.containerStyle}>
13
16
  <Text style={style.textStyle}>{meetingTitle}</Text>
14
17
  </View>
18
+ {getAILayoutType() !== 'LAYOUT_TYPE_2' ? (
19
+ <View>
20
+ <SettingButton />
21
+ </View>
22
+ ) : (
23
+ <></>
24
+ )}
15
25
  </View>
16
26
  );
17
27
  };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import {CustomizationApiInterface} from 'customization-api';
3
- import {isMobileUA} from '../utils/common';
3
+ import {isIOS, isAndroid, isMobileUA} from '../utils/common';
4
4
  import Bottombar from './components/Bottombar';
5
5
  import CustomCreate from './components/CustomCreate';
6
6
  import MobileTopBar from './components/mobile/Topbar';
@@ -8,24 +8,20 @@ import MobileBottombar from './components/mobile/Bottombar';
8
8
  import CustomChatPanel from './components/CustomChatPanel';
9
9
  import CustomSettingsPanel from './components/CustomSettingsPanel';
10
10
  import {AgentProvider} from './components/AgentControls/AgentContext';
11
-
11
+ import {getAILayoutType} from './utils';
12
12
  //LAYOUT_TYPE_1
13
- import DefaultLayout from './layout/DefaultLayout';
13
+ import NewAnimationLayout from './layout/NewAnimation';
14
14
  //LAYOUT_TYPE_2
15
- import ConversationalAILayout from './layout/ConversationalAI';
15
+ import AIWithLocalUser from './layout/AIWithLocalUser';
16
16
  //LAYOUT_TYPE_3
17
- import NewAnimationLayout from './layout/NewAnimation';
17
+ import ConversationalAILayout from './layout/ConversationalAI';
18
18
 
19
19
  const DummyComponent = () => {
20
20
  return <></>;
21
21
  };
22
22
 
23
- const getAILayoutType = () => {
24
- return $config.AI_LAYOUT ? $config.AI_LAYOUT : 'LAYOUT_TYPE_1';
25
- };
26
-
27
23
  const getTopBarComponent = () => {
28
- return isMobileUA() || getAILayoutType() !== 'LAYOUT_TYPE_1'
24
+ return isMobileUA() || getAILayoutType() !== 'LAYOUT_TYPE_2'
29
25
  ? MobileTopBar
30
26
  : DummyComponent;
31
27
  };
@@ -33,17 +29,19 @@ const getTopBarComponent = () => {
33
29
  const getBottombarComponent = () => {
34
30
  return isMobileUA()
35
31
  ? MobileBottombar
36
- : getAILayoutType() === 'LAYOUT_TYPE_1'
32
+ : getAILayoutType() === 'LAYOUT_TYPE_2'
37
33
  ? Bottombar
38
34
  : DummyComponent;
39
35
  };
40
36
 
41
37
  const getCustomLayoutComponent = () => {
42
- return getAILayoutType() === 'LAYOUT_TYPE_3'
43
- ? NewAnimationLayout
44
- : getAILayoutType() === 'LAYOUT_TYPE_2'
38
+ return isAndroid() || isIOS()
39
+ ? AIWithLocalUser
40
+ : getAILayoutType() === 'LAYOUT_TYPE_3'
45
41
  ? ConversationalAILayout
46
- : DefaultLayout;
42
+ : getAILayoutType() === 'LAYOUT_TYPE_2'
43
+ ? AIWithLocalUser
44
+ : NewAnimationLayout;
47
45
  };
48
46
 
49
47
  export const AI_AGENT_CUSTOMIZATION: CustomizationApiInterface = {
@@ -14,7 +14,7 @@ import AudioVisualizer, {DisconnectedView} from '../components/AudioVisualizer';
14
14
  import {AgentState} from '../components/AgentControls/const';
15
15
  import {AgentContext} from '../components/AgentControls/AgentContext';
16
16
 
17
- export default function DefaultLayout() {
17
+ export default function AIWithLocalUser() {
18
18
  const localUid = useLocalUid();
19
19
  const {defaultContent, activeUids} = useContent();
20
20
  const {RtcEngineUnsafe} = useRtc();
@@ -7,18 +7,17 @@ import {
7
7
  Image,
8
8
  ActivityIndicator,
9
9
  } from 'react-native';
10
- import {useSidePanel, isAndroid, isIOS} from 'customization-api';
10
+ import {useSidePanel} from 'customization-api';
11
11
  import ThemeConfig from '../../theme';
12
12
  import {AgentContext} from '../components/AgentControls/AgentContext';
13
13
  import {AgentState} from '../components/AgentControls/const';
14
14
  import {useIsAgentAvailable} from '../components/utils';
15
- import {isMobileUA} from '../../utils/common';
15
+ import {isMobileUA, isAndroid, isIOS} from '../../utils/common';
16
16
  //@ts-ignore
17
17
  import JoinCallIcon from '../assets/join-call.png';
18
18
  import {
19
19
  DisconnectButton,
20
20
  MicButton,
21
- SettingButton,
22
21
  TranscriptButton,
23
22
  } from '../components/ControlButtons';
24
23
 
@@ -39,9 +38,15 @@ export default function ConversationalAI() {
39
38
  const {Application} = require('@splinetool/runtime');
40
39
  // start the application and load the scene
41
40
  const spline = new Application(canvas);
42
- spline.load(
43
- 'https://d1i64xs2div6cu.cloudfront.net/scene-250216.splinecode',
44
- );
41
+ spline
42
+ ?.load(
43
+ 'https://d1i64xs2div6cu.cloudfront.net/scene-250216.splinecode',
44
+ )
45
+ ?.then(() => {
46
+ if (isMobileUA()) {
47
+ spline?.setZoom(0.5);
48
+ }
49
+ });
45
50
  }
46
51
  });
47
52
  }
@@ -83,7 +88,6 @@ export default function ConversationalAI() {
83
88
  <View style={styles.controlsContainer}>
84
89
  <MicButton />
85
90
  <TranscriptButton />
86
- <SettingButton />
87
91
  <DisconnectButton />
88
92
  </View>
89
93
  ) : (
@@ -94,6 +98,7 @@ export default function ConversationalAI() {
94
98
  agentConnectionState === AgentState.AGENT_DISCONNECT_REQUEST
95
99
  ? {backgroundColor: $config.SEMANTIC_ERROR}
96
100
  : {},
101
+ isLoading || !isAgentAvailable ? styles.disabledOpacity : {},
97
102
  ]}
98
103
  onPress={() => {
99
104
  toggleAgentConnection();
@@ -142,7 +147,7 @@ const styles = StyleSheet.create({
142
147
  },
143
148
  layoutRootContainer: {
144
149
  flex: 1,
145
- backgroundColor: $config.VIDEO_AUDIO_TILE_COLOR,
150
+ backgroundColor: '#1D1D1D',
146
151
  borderRadius: 8,
147
152
  },
148
153
  callAgentBtnInnerContainer: {
@@ -158,6 +163,9 @@ const styles = StyleSheet.create({
158
163
  display: 'flex',
159
164
  flex: 1,
160
165
  },
166
+ disabledOpacity: {
167
+ opacity: 0.6,
168
+ },
161
169
  callAgentBtnText: {
162
170
  fontFamily: ThemeConfig.FontFamily.sansPro,
163
171
  fontSize: 18,
@@ -1,4 +1,4 @@
1
- import React, {useContext} from 'react';
1
+ import React, {useContext, useEffect} from 'react';
2
2
  import {
3
3
  StyleSheet,
4
4
  TouchableOpacity,
@@ -15,15 +15,18 @@ import AiAgentCustomView from '../ai-interface/AIAgentInterface';
15
15
  import {
16
16
  DisconnectButton,
17
17
  MicButton,
18
- SettingButton,
19
18
  TranscriptButton,
20
19
  } from '../components/ControlButtons';
20
+ import {isMobileUA} from '../../utils/common';
21
+ import {useSidePanel} from 'customization-api';
22
+
21
23
  //@ts-ignore
22
24
  import JoinCallIcon from '../assets/join-call.png';
23
25
 
24
26
  export default function NewAnimation() {
25
27
  const {agentConnectionState, toggleAgentConnection} =
26
28
  useContext(AgentContext);
29
+ const {setSidePanel} = useSidePanel();
27
30
 
28
31
  const isLoading =
29
32
  agentConnectionState === AgentState.REQUEST_SENT ||
@@ -36,6 +39,12 @@ export default function NewAnimation() {
36
39
  isAwaitingLeave;
37
40
  const isAgentAvailable = useIsAgentAvailable();
38
41
 
42
+ useEffect(() => {
43
+ setTimeout(() => {
44
+ !isMobileUA() && setSidePanel('custom-settings-panel');
45
+ });
46
+ }, []);
47
+
39
48
  return (
40
49
  <View style={styles.layoutRootContainer}>
41
50
  <View style={styles.container}>
@@ -46,7 +55,6 @@ export default function NewAnimation() {
46
55
  <View style={styles.controlsContainer}>
47
56
  <MicButton />
48
57
  <TranscriptButton />
49
- <SettingButton />
50
58
  <DisconnectButton />
51
59
  </View>
52
60
  ) : (
@@ -57,6 +65,7 @@ export default function NewAnimation() {
57
65
  agentConnectionState === AgentState.AGENT_DISCONNECT_REQUEST
58
66
  ? {backgroundColor: $config.SEMANTIC_ERROR}
59
67
  : {},
68
+ isLoading || !isAgentAvailable ? styles.disabledOpacity : {},
60
69
  ]}
61
70
  onPress={() => {
62
71
  toggleAgentConnection();
@@ -109,7 +118,6 @@ const styles = StyleSheet.create({
109
118
  },
110
119
  layoutRootContainer: {
111
120
  flex: 1,
112
- backgroundColor: $config.VIDEO_AUDIO_TILE_COLOR,
113
121
  borderRadius: 8,
114
122
  },
115
123
  callAgentBtnInnerContainer: {
@@ -125,6 +133,9 @@ const styles = StyleSheet.create({
125
133
  display: 'flex',
126
134
  flex: 1,
127
135
  },
136
+ disabledOpacity: {
137
+ opacity: 0.6,
138
+ },
128
139
  callAgentBtnText: {
129
140
  fontFamily: ThemeConfig.FontFamily.sansPro,
130
141
  fontSize: 18,
@@ -140,6 +151,7 @@ const styles = StyleSheet.create({
140
151
  marginRight: 'auto',
141
152
  maxWidth: 500,
142
153
  bottom: 50,
154
+ zIndex: 999,
143
155
  },
144
156
  container: {
145
157
  flex: 1,
@@ -1,5 +1,7 @@
1
1
  import {IMicrophoneAudioTrack} from 'agora-rtc-sdk-ng';
2
2
  import {useState, useEffect} from 'react';
3
+ import {isAndroid, isIOS} from '../utils/common';
4
+
3
5
  //for native its require text decoder to be imported
4
6
  const TextDecoder = require('text-encoding').TextDecoder;
5
7
  export const useMultibandTrackVolume = (
@@ -131,3 +133,11 @@ export const Base64 = {
131
133
  return output;
132
134
  },
133
135
  };
136
+
137
+ export const getAILayoutType = () => {
138
+ return isAndroid() || isIOS()
139
+ ? 'LAYOUT_TYPE_2'
140
+ : $config.AI_LAYOUT
141
+ ? $config.AI_LAYOUT
142
+ : 'LAYOUT_TYPE_1';
143
+ };
@@ -52,7 +52,7 @@ const useSpeechToText = () => {
52
52
  useEffect(() => {
53
53
  if (!$config.ENABLE_STT) {
54
54
  //throw new Error('Speech To Text is not enabled');
55
- console.error('Speech To Text is not enabled');
55
+ console.log('Speech To Text is not enabled');
56
56
  }
57
57
  }, []);
58
58