@revrag-ai/embed-react-native 1.0.26 → 1.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +5 -0
  2. package/android/build.gradle +1 -0
  3. package/dist/commonjs/api/api.js.map +1 -1
  4. package/dist/commonjs/assets/fonts/PlaywriteNZBasic-ExtraLight.ttf +0 -0
  5. package/dist/commonjs/assets/fonts/PlaywriteNZBasic-Light.ttf +0 -0
  6. package/dist/commonjs/assets/fonts/PlaywriteNZBasic-Regular.ttf +0 -0
  7. package/dist/commonjs/assets/fonts/PlaywriteNZBasic-Thin.ttf +0 -0
  8. package/dist/commonjs/components/DynamicComponent/Typewriter.js +124 -0
  9. package/dist/commonjs/components/DynamicComponent/Typewriter.js.map +1 -0
  10. package/dist/commonjs/components/Embed/EmbedButton.js +362 -113
  11. package/dist/commonjs/components/Embed/EmbedButton.js.map +1 -1
  12. package/dist/commonjs/components/styles/EmbedButton.style.js +112 -158
  13. package/dist/commonjs/components/styles/EmbedButton.style.js.map +1 -1
  14. package/dist/commonjs/context/EmbedProvider.js +187 -14
  15. package/dist/commonjs/context/EmbedProvider.js.map +1 -1
  16. package/dist/commonjs/events/clickEventTracker.js +210 -0
  17. package/dist/commonjs/events/clickEventTracker.js.map +1 -0
  18. package/dist/commonjs/events/embed.event.js +11 -3
  19. package/dist/commonjs/events/embed.event.js.map +1 -1
  20. package/dist/commonjs/hooks/EmbedButton.animations.js +115 -15
  21. package/dist/commonjs/hooks/EmbedButton.animations.js.map +1 -1
  22. package/dist/commonjs/hooks/EmbedButton.helpers.js +11 -3
  23. package/dist/commonjs/hooks/EmbedButton.helpers.js.map +1 -1
  24. package/dist/commonjs/hooks/EmbedButton.hooks.js +17 -11
  25. package/dist/commonjs/hooks/EmbedButton.hooks.js.map +1 -1
  26. package/dist/commonjs/hooks/voiceagent.js +34 -12
  27. package/dist/commonjs/hooks/voiceagent.js.map +1 -1
  28. package/dist/commonjs/index.js +6 -2
  29. package/dist/commonjs/index.js.map +1 -1
  30. package/dist/commonjs/utils/constant.js +6 -1
  31. package/dist/commonjs/utils/constant.js.map +1 -1
  32. package/dist/commonjs/utils/permision.js +42 -1
  33. package/dist/commonjs/utils/permision.js.map +1 -1
  34. package/dist/commonjs/utils/reanimated.helper.js +4 -0
  35. package/dist/commonjs/utils/reanimated.helper.js.map +1 -1
  36. package/dist/module/api/api.js.map +1 -1
  37. package/dist/module/assets/fonts/PlaywriteNZBasic-ExtraLight.ttf +0 -0
  38. package/dist/module/assets/fonts/PlaywriteNZBasic-Light.ttf +0 -0
  39. package/dist/module/assets/fonts/PlaywriteNZBasic-Regular.ttf +0 -0
  40. package/dist/module/assets/fonts/PlaywriteNZBasic-Thin.ttf +0 -0
  41. package/dist/module/components/DynamicComponent/Typewriter.js +122 -0
  42. package/dist/module/components/DynamicComponent/Typewriter.js.map +1 -0
  43. package/dist/module/components/Embed/EmbedButton.js +367 -118
  44. package/dist/module/components/Embed/EmbedButton.js.map +1 -1
  45. package/dist/module/components/styles/EmbedButton.style.js +111 -157
  46. package/dist/module/components/styles/EmbedButton.style.js.map +1 -1
  47. package/dist/module/context/EmbedProvider.js +187 -14
  48. package/dist/module/context/EmbedProvider.js.map +1 -1
  49. package/dist/module/events/clickEventTracker.js +199 -0
  50. package/dist/module/events/clickEventTracker.js.map +1 -0
  51. package/dist/module/events/embed.event.js +11 -3
  52. package/dist/module/events/embed.event.js.map +1 -1
  53. package/dist/module/hooks/EmbedButton.animations.js +109 -13
  54. package/dist/module/hooks/EmbedButton.animations.js.map +1 -1
  55. package/dist/module/hooks/EmbedButton.helpers.js +10 -2
  56. package/dist/module/hooks/EmbedButton.helpers.js.map +1 -1
  57. package/dist/module/hooks/EmbedButton.hooks.js +17 -11
  58. package/dist/module/hooks/EmbedButton.hooks.js.map +1 -1
  59. package/dist/module/hooks/voiceagent.js +34 -12
  60. package/dist/module/hooks/voiceagent.js.map +1 -1
  61. package/dist/module/index.js +8 -3
  62. package/dist/module/index.js.map +1 -1
  63. package/dist/module/utils/constant.js +6 -1
  64. package/dist/module/utils/constant.js.map +1 -1
  65. package/dist/module/utils/permision.js +42 -1
  66. package/dist/module/utils/permision.js.map +1 -1
  67. package/dist/module/utils/reanimated.helper.js +4 -0
  68. package/dist/module/utils/reanimated.helper.js.map +1 -1
  69. package/dist/typescript/src/api/api.d.ts.map +1 -1
  70. package/dist/typescript/src/components/DynamicComponent/Typewriter.d.ts +15 -0
  71. package/dist/typescript/src/components/DynamicComponent/Typewriter.d.ts.map +1 -0
  72. package/dist/typescript/src/components/Embed/EmbedButton.d.ts +7 -1
  73. package/dist/typescript/src/components/Embed/EmbedButton.d.ts.map +1 -1
  74. package/dist/typescript/src/components/styles/EmbedButton.style.d.ts +22 -114
  75. package/dist/typescript/src/components/styles/EmbedButton.style.d.ts.map +1 -1
  76. package/dist/typescript/src/context/EmbedProvider.d.ts +30 -0
  77. package/dist/typescript/src/context/EmbedProvider.d.ts.map +1 -1
  78. package/dist/typescript/src/events/__tests__/agent-event-emitter.test.d.ts +5 -0
  79. package/dist/typescript/src/events/__tests__/agent-event-emitter.test.d.ts.map +1 -0
  80. package/dist/typescript/src/events/__tests__/clickEventTracker.test.d.ts +5 -0
  81. package/dist/typescript/src/events/__tests__/clickEventTracker.test.d.ts.map +1 -0
  82. package/dist/typescript/src/events/__tests__/embed.event.test.d.ts +5 -0
  83. package/dist/typescript/src/events/__tests__/embed.event.test.d.ts.map +1 -0
  84. package/dist/typescript/src/events/__tests__/embed.validators.test.d.ts +5 -0
  85. package/dist/typescript/src/events/__tests__/embed.validators.test.d.ts.map +1 -0
  86. package/dist/typescript/src/events/clickEventTracker.d.ts +70 -0
  87. package/dist/typescript/src/events/clickEventTracker.d.ts.map +1 -0
  88. package/dist/typescript/src/events/embed.event.d.ts.map +1 -1
  89. package/dist/typescript/src/hooks/EmbedButton.animations.d.ts +29 -6
  90. package/dist/typescript/src/hooks/EmbedButton.animations.d.ts.map +1 -1
  91. package/dist/typescript/src/hooks/EmbedButton.helpers.d.ts +60 -8
  92. package/dist/typescript/src/hooks/EmbedButton.helpers.d.ts.map +1 -1
  93. package/dist/typescript/src/hooks/EmbedButton.hooks.d.ts +4 -2
  94. package/dist/typescript/src/hooks/EmbedButton.hooks.d.ts.map +1 -1
  95. package/dist/typescript/src/hooks/types/voiceAgent.types.d.ts +2 -0
  96. package/dist/typescript/src/hooks/types/voiceAgent.types.d.ts.map +1 -1
  97. package/dist/typescript/src/hooks/voiceagent.d.ts.map +1 -1
  98. package/dist/typescript/src/index.d.ts +8 -9
  99. package/dist/typescript/src/index.d.ts.map +1 -1
  100. package/dist/typescript/src/utils/constant.d.ts +1 -1
  101. package/dist/typescript/src/utils/constant.d.ts.map +1 -1
  102. package/dist/typescript/src/utils/permision.d.ts.map +1 -1
  103. package/dist/typescript/src/utils/reanimated.helper.d.ts +2 -0
  104. package/dist/typescript/src/utils/reanimated.helper.d.ts.map +1 -1
  105. package/package.json +3 -8
  106. package/react-native.config.js +1 -0
  107. package/revrag-ai-embed-react-native.podspec +1 -0
  108. package/src/assets/fonts/PlaywriteNZBasic-ExtraLight.ttf +0 -0
  109. package/src/assets/fonts/PlaywriteNZBasic-Light.ttf +0 -0
  110. package/src/assets/fonts/PlaywriteNZBasic-Regular.ttf +0 -0
  111. package/src/assets/fonts/PlaywriteNZBasic-Thin.ttf +0 -0
@@ -6,28 +6,30 @@
6
6
  * Features: draggable, expandable, animated, with call controls and auto-trigger support.
7
7
  */
8
8
 
9
+ import { ConnectionState } from 'livekit-client';
9
10
  import LottieView from 'lottie-react-native';
10
- import { useEffect, useMemo, useRef, useState } from 'react';
11
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
11
12
  import { Image, Text, TouchableOpacity, View } from 'react-native';
12
- import { GestureDetector } from 'react-native-gesture-handler';
13
+ import { GestureDetector, ScrollView } from 'react-native-gesture-handler';
13
14
  import LinearGradient from 'react-native-linear-gradient';
15
+ import Embed, { AgentEvent, EventKeys } from "../../events/embed.event.js";
14
16
  import { useVoiceAgent } from "../../hooks/voiceagent.js";
15
17
  import { createEmbedButtonStyles } from "../styles/EmbedButton.style.js";
16
18
  import { WaveformVisualizer } from "./EmbedAudioWave.js";
17
19
  import Voice from "./EmbedVoice.js";
18
- import Embed, { AgentEvent } from "../../events/embed.event.js";
19
20
 
20
21
  // Helpers and constants
21
- import { BUTTON_DIMENSIONS, DEFAULT_GRADIENT_COLORS, formatDuration, ICON_URLS } from "../../hooks/EmbedButton.helpers.js";
22
+ import { BUTTON_DIMENSIONS, DEFAULT_GRADIENT_COLORS, FOOTER_SAFE_INSET, formatDuration, ICON_URLS } from "../../hooks/EmbedButton.helpers.js";
23
+ import Typewriter from "../DynamicComponent/Typewriter.js";
22
24
 
23
25
  // Custom hooks
24
26
  import { useCallDuration, useCallManagement, useConfigData, useInactivityBehavior } from "../../hooks/EmbedButton.hooks.js";
25
27
 
26
28
  // Animation hooks
27
- import { Animated, createPanGesture, useAnimationValues, useBreathingAnimation, useButtonAnimatedStyles, useButtonAnimations, usePopupAnimatedStyles } from "../../hooks/EmbedButton.animations.js";
29
+ import { Animated, createPanGesture, useAnimationValues, useBreathingAnimation, useButtonAnimatedStyles, useButtonAnimations, useExpandedContentAnimatedStyles, useInputSectionAnimations, usePopupAnimatedStyles } from "../../hooks/EmbedButton.animations.js";
28
30
 
29
31
  // ==================== STYLES CONFIG ====================
30
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
32
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
31
33
  const defaultStyles = {
32
34
  buttonWidth: BUTTON_DIMENSIONS.WIDTH,
33
35
  buttonHeight: BUTTON_DIMENSIONS.HEIGHT,
@@ -44,35 +46,57 @@ const defaultStyles = {
44
46
  };
45
47
 
46
48
  // ==================== MAIN COMPONENT ====================
49
+
47
50
  /**
48
51
  * EmbedButton - Main voice agent floating action button
49
52
  *
50
53
  * @example
51
54
  * ```tsx
52
55
  * <EmbedButton />
56
+ * <EmbedButton containerInset={{ right: 16, bottom: 24 }} />
53
57
  * ```
54
58
  */
55
- export function EmbedButton() {
59
+ export function EmbedButton({
60
+ containerInset
61
+ } = {}) {
56
62
  // ==================== VOICE AGENT STATE ====================
57
63
  const {
58
- initializeVoiceAgent,
59
64
  tokenDetails,
60
- endCall,
61
65
  isLoading,
62
66
  isMicMuted,
67
+ connectionState,
68
+ roomRef,
63
69
  muteMic,
70
+ endCall,
64
71
  unmuteMic,
65
- connectionState,
66
- roomRef
72
+ clearDataReceived,
73
+ dataTranscription: livekitData,
74
+ initializeVoiceAgent
67
75
  } = useVoiceAgent();
68
76
 
69
77
  // ==================== LOCAL STATE ====================
70
78
  const [isOpen, setIsOpen] = useState(false);
79
+ const [expandDirection, setExpandDirection] = useState('right');
71
80
  const lottieRef = useRef(null);
72
- const styles = useMemo(() => createEmbedButtonStyles(defaultStyles), []);
81
+ const calculationScrollRef = useRef(null);
82
+ const userScrolledUpRef = useRef(false);
83
+ const SCROLL_END_THRESHOLD = 20;
84
+
85
+ // Reset "user scrolled up" when calculation content changes so new content auto-scrolls to bottom
86
+ useEffect(() => {
87
+ if (livekitData?.type === 'calculation') {
88
+ userScrolledUpRef.current = false;
89
+ }
90
+ }, [livekitData?.list, livekitData?.type]);
73
91
 
74
92
  // ==================== CUSTOM HOOKS ====================
75
93
  const configData = useConfigData();
94
+ const styles = useMemo(() => createEmbedButtonStyles({
95
+ ...defaultStyles,
96
+ ...(containerInset != null && {
97
+ containerInset
98
+ })
99
+ }, configData), [configData, containerInset]);
76
100
  const {
77
101
  callDuration,
78
102
  resetDuration
@@ -82,13 +106,14 @@ export function EmbedButton() {
82
106
  handleEndCall,
83
107
  handleMicToggle
84
108
  } = useCallManagement({
85
- initializeVoiceAgent,
109
+ isOpen,
110
+ isMicMuted,
86
111
  endCall,
87
112
  muteMic,
113
+ setIsOpen,
88
114
  unmuteMic,
89
- isMicMuted,
90
115
  resetDuration,
91
- setIsOpen
116
+ initializeVoiceAgent
92
117
  });
93
118
  const {
94
119
  isAutoOpen,
@@ -109,86 +134,175 @@ export function EmbedButton() {
109
134
  start,
110
135
  menuAnimation,
111
136
  buttonWidth,
112
- buttonScale
137
+ buttonScale,
138
+ buttonHeight,
139
+ inputSectionProgress
113
140
  } = animationValues;
114
- useButtonAnimations(isOpen, menuAnimation, buttonWidth);
141
+ useButtonAnimations(isOpen || livekitData && Object.keys(livekitData).length > 0, menuAnimation, buttonWidth, offset, start, expandDirection);
142
+ useInputSectionAnimations(livekitData && Object.keys(livekitData).length > 0, inputSectionProgress, buttonHeight);
115
143
  useBreathingAnimation(isOpen, isAutoOpen, buttonScale);
116
- const buttonAnimatedStyles = useButtonAnimatedStyles(isOpen, offset, buttonWidth, isPressed, buttonScale);
117
- const popupAnimatedStyles = usePopupAnimatedStyles(offset, isPressed);
118
- const panGesture = useMemo(() => createPanGesture(isPressed, offset, start, isOpen, setIsAutoOpen), [isPressed, offset, start, isOpen, setIsAutoOpen]);
144
+ const buttonAnimatedStyles = useButtonAnimatedStyles(isOpen, offset, buttonWidth, isPressed, buttonScale, buttonHeight);
145
+ const expandedContentAnimatedStyles = useExpandedContentAnimatedStyles(menuAnimation);
146
+ const popupAnimatedStyles = usePopupAnimatedStyles(offset, isPressed, expandDirection);
147
+ const panGesture = useMemo(() => createPanGesture(isPressed, offset, start, isOpen, setIsAutoOpen, setExpandDirection), [isPressed, offset, start, isOpen, setIsAutoOpen]);
119
148
 
120
149
  // ==================== AGENT EVENT EMISSIONS ====================
121
150
  // Emit agent connected/disconnected events based on connection state
151
+ // Track previous connection state to properly detect disconnection
152
+ const prevConnectionStateRef = useRef(connectionState);
153
+ const lastEmittedEventRef = useRef(null);
122
154
  useEffect(() => {
123
- const emitConnectionEvent = async () => {
124
- if (connectionState === 'connected') {
125
- await Embed.event.emit(AgentEvent.AGENT_CONNECTED, {
126
- timestamp: new Date().toISOString(),
127
- metadata: {
128
- callDuration: 0
129
- }
130
- });
131
- } else if (connectionState === 'disconnected' && callDuration > 0) {
132
- // Only emit disconnected if we were previously connected
133
- await Embed.event.emit(AgentEvent.AGENT_DISCONNECTED, {
134
- timestamp: new Date().toISOString(),
135
- metadata: {
136
- callDuration
137
- }
138
- });
155
+ const prevState = prevConnectionStateRef.current;
156
+
157
+ // Check if we should emit connected event
158
+ if (connectionState === ConnectionState.Connected && prevState !== ConnectionState.Connected) {
159
+ // Prevent duplicate connected events
160
+ if (lastEmittedEventRef.current?.type === 'connected' && lastEmittedEventRef.current?.state === connectionState) {
161
+ return;
139
162
  }
140
- };
141
- emitConnectionEvent().catch(error => {
142
- console.error('Error emitting connection event:', error);
143
- });
144
- }, [connectionState]);
145
163
 
146
- // Emit popup visibility events when isAutoOpen changes
164
+ // Update refs IMMEDIATELY (synchronously) before async emit
165
+ prevConnectionStateRef.current = connectionState;
166
+ lastEmittedEventRef.current = {
167
+ type: 'connected',
168
+ state: connectionState
169
+ };
170
+
171
+ // Emit connected when entering Connected state
172
+ Embed.event.emit(AgentEvent.AGENT_CONNECTED, {
173
+ timestamp: new Date().toISOString(),
174
+ metadata: {
175
+ callDuration: 0
176
+ }
177
+ }).catch(error => {
178
+ console.error('Error emitting connected event:', error);
179
+ });
180
+
181
+ // Send analytics to backend when agent conversation has started (connected)
182
+ Embed.Event(EventKeys.ANALYTICS_DATA, {
183
+ event_name: 'agent_conversation_started'
184
+ }).catch(error => {
185
+ console.error('Error sending agent_conversation_started analytics:', error);
186
+ });
187
+ } else if (prevState === ConnectionState.Connected && connectionState !== ConnectionState.Connected) {
188
+ // Prevent duplicate disconnected events
189
+ if (lastEmittedEventRef.current?.type === 'disconnected') {
190
+ return;
191
+ }
192
+
193
+ // Update refs IMMEDIATELY (synchronously) before async emit
194
+ prevConnectionStateRef.current = connectionState;
195
+ lastEmittedEventRef.current = {
196
+ type: 'disconnected',
197
+ state: connectionState
198
+ };
199
+
200
+ // Emit disconnected when LEAVING Connected state (to Connecting or Disconnected)
201
+ // This catches the transition: Connected -> Connecting -> Disconnected
202
+ Embed.event.emit(AgentEvent.AGENT_DISCONNECTED, {
203
+ timestamp: new Date().toISOString(),
204
+ metadata: {
205
+ callDuration
206
+ }
207
+ }).catch(error => {
208
+ console.error('Error emitting disconnected event:', error);
209
+ });
210
+ } else {
211
+ // Update previous state for other transitions
212
+ prevConnectionStateRef.current = connectionState;
213
+ }
214
+ }, [connectionState, callDuration]); // Include callDuration to get fresh value
215
+
216
+ // Emit popup_message_visible when the popup is actually shown (isAutoOpen && !isOpen)
147
217
  useEffect(() => {
218
+ const popupVisible = isAutoOpen && !isOpen;
219
+ if (!popupVisible) return;
148
220
  const emitPopupEvent = async () => {
149
- await Embed.event.emit(AgentEvent.POPUP_MESSAGE_VISIBLE, {
150
- value: isAutoOpen,
221
+ await Embed.Event(EventKeys.ANALYTICS_DATA, {
222
+ event_name: 'popup_message_visible',
151
223
  metadata: {
152
- trigger: isAutoOpen ? 'auto_inactivity' : 'manual_dismiss'
224
+ value: true,
225
+ trigger: 'auto_inactivity'
153
226
  }
154
227
  });
155
228
  };
156
229
  emitPopupEvent().catch(error => {
157
230
  console.error('Error emitting popup visibility event:', error);
158
231
  });
159
- }, [isAutoOpen]);
232
+ }, [isAutoOpen, isOpen]);
233
+
234
+ // Emit gen_tool_triggered analytics when livekit data is first received
235
+ const prevLivekitDataRef = useRef(false);
236
+ useEffect(() => {
237
+ const isLivekitData = livekitData && Object.keys(livekitData).length > 0;
238
+ if (isLivekitData && !prevLivekitDataRef.current) {
239
+ prevLivekitDataRef.current = true;
240
+ Embed.Event(EventKeys.ANALYTICS_DATA, {
241
+ event_name: 'gen_tool_triggered',
242
+ metadata: {
243
+ source: 'livekit_data'
244
+ }
245
+ }).catch(error => {
246
+ console.error('Error sending gen_tool_triggered analytics:', error);
247
+ });
248
+ }
249
+ if (!isLivekitData) {
250
+ prevLivekitDataRef.current = false;
251
+ }
252
+ }, [livekitData]);
160
253
 
161
254
  // ==================== HANDLERS ====================
162
- const handleButtonPress = () => {
163
- setIsOpen(!isOpen);
255
+ const handleButtonPress = useCallback(async () => {
256
+ const eventName = isOpen ? 'agent_tap_to_close' : 'agent_tap_to_open';
257
+ try {
258
+ await Embed.Event(EventKeys.ANALYTICS_DATA, {
259
+ event_name: eventName,
260
+ metadata: {
261
+ button_pressed: 'button_pressed'
262
+ }
263
+ });
264
+ } catch (error) {
265
+ console.error('Error sending button_pressed analytics:', error);
266
+ }
267
+ setIsOpen(prev => !prev);
164
268
  setIsAutoOpen(false);
165
- };
166
- const handleConnected = () => {
167
- // Hook for handling successful connection
168
- };
269
+ }, [isOpen, setIsAutoOpen]);
270
+ const handleConnected = useCallback(() => {
271
+ // Reserved for connection success side effects (e.g. analytics)
272
+ }, []);
169
273
 
170
274
  // ==================== COMPUTED VALUES ====================
171
275
  const lottieSource = useMemo(() => ({
172
- uri: configData?.icon_animation || ICON_URLS.AMPLIFY_ANIMATION
173
- }), [configData?.icon_animation]);
276
+ uri: configData?.agentAvatar?.avatarUrl || ICON_URLS.AMPLIFY_ANIMATION
277
+ }), [configData?.agentAvatar?.avatarUrl]);
278
+ const micIconSource = useMemo(() => ({
279
+ uri: isMicMuted ? ICON_URLS.MIC_OFF : ICON_URLS.MIC_ON
280
+ }), [isMicMuted]);
174
281
  const statusText = useMemo(() => {
175
282
  if (isLoading) return 'Connecting...';
176
283
  if (tokenDetails?.token) return formatDuration(callDuration);
177
- return configData?.agent_type || 'Onboarding Agent';
178
- }, [isLoading, tokenDetails?.token, callDuration, configData?.agent_type]);
179
- const gradientColors = configData?.gradient || DEFAULT_GRADIENT_COLORS;
180
- const agentName = configData?.agent_name || 'Your AI Agent';
181
- const popupText = configData?.popup_description || 'Any doubts? Ask agent now';
182
- const connectButtonText = configData?.connect_button_title || 'Start Call';
284
+ return configData?.agentTextContent?.agentType ?? 'Onboarding Agent';
285
+ }, [isLoading, tokenDetails?.token, callDuration, configData?.agentTextContent?.agentType]);
286
+ const gradientColors = configData?.colorPalette?.grad1 != null && configData?.colorPalette?.grad2 != null ? [configData.colorPalette.grad1, configData.colorPalette.grad2] : configData?.colorPalette?.grad1 != null && configData?.colorPalette?.grad2 != null ? [configData.colorPalette.grad1, configData.colorPalette.grad2] : DEFAULT_GRADIENT_COLORS;
287
+ const agentName = configData?.agentTextContent?.agentName ?? 'Your AI Agent';
288
+ const popupText = configData?.collapsedView?.tooltipMessage ?? 'Any doubts? Ask agent now';
289
+ const connectButtonText = configData?.agentTextContent?.buttonText ?? 'Start Call';
290
+ const isLivekitData = livekitData && Object.keys(livekitData).length > 0;
291
+ const gradientStyle = useMemo(() => [styles.linearGradient, isLivekitData ? styles.linearGradientColumn : isOpen ? styles.expandedLinearGradient : styles.collapsedLinearGradient], [isOpen, isLivekitData, styles]);
292
+ const isConnected = !!tokenDetails?.token;
183
293
 
184
294
  // ==================== EARLY RETURNS ====================
185
295
  if (!configData) return null;
186
296
 
187
297
  // ==================== RENDER ====================
188
298
  return /*#__PURE__*/_jsxs(View, {
189
- style: styles.container,
299
+ style: [styles.container, isLivekitData && {
300
+ bottom: FOOTER_SAFE_INSET
301
+ }],
190
302
  children: [isAutoOpen && !isOpen && /*#__PURE__*/_jsx(Animated.View, {
191
303
  style: [popupAnimatedStyles, styles.popupContainer],
304
+ accessibilityLabel: popupText,
305
+ accessibilityLiveRegion: "polite",
192
306
  children: /*#__PURE__*/_jsx(Text, {
193
307
  style: styles.popupText,
194
308
  children: popupText
@@ -197,7 +311,7 @@ export function EmbedButton() {
197
311
  gesture: panGesture,
198
312
  children: /*#__PURE__*/_jsx(Animated.View, {
199
313
  pointerEvents: "auto",
200
- style: [styles.button, buttonAnimatedStyles, styles.buttonContent],
314
+ style: [styles.button, buttonAnimatedStyles, isLivekitData ? styles.expandedButtonWhite : styles.buttonContent],
201
315
  children: /*#__PURE__*/_jsxs(LinearGradient, {
202
316
  colors: gradientColors,
203
317
  start: {
@@ -208,7 +322,7 @@ export function EmbedButton() {
208
322
  x: 1,
209
323
  y: 0
210
324
  },
211
- style: [styles.linearGradient, isOpen ? styles.expandedLinearGradient : styles.collapsedLinearGradient],
325
+ style: gradientStyle,
212
326
  angle: 0,
213
327
  angleCenter: {
214
328
  x: 0.5,
@@ -220,67 +334,202 @@ export function EmbedButton() {
220
334
  onDisconnected: handleEndCall,
221
335
  roomRef: roomRef,
222
336
  onConnected: handleConnected
223
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
224
- onPress: handleButtonPress,
225
- style: styles.pressable,
226
- children: /*#__PURE__*/_jsx(LottieView, {
227
- ref: lottieRef,
228
- source: lottieSource,
229
- autoPlay: true,
230
- loop: true,
231
- style: styles.iconImage,
232
- enableMergePathsAndroidForKitKatAndAbove: true,
233
- enableSafeModeAndroid: true
234
- })
235
- }), isOpen && /*#__PURE__*/_jsxs(View, {
236
- style: styles.expandedContentContainer,
337
+ }), isLivekitData ? /*#__PURE__*/_jsxs(View, {
338
+ style: styles.expandedCallViewWrapper,
237
339
  children: [/*#__PURE__*/_jsxs(View, {
238
- style: styles.leftContentSection,
239
- children: [/*#__PURE__*/_jsx(Text, {
240
- style: [styles.agentNameText, styles.leftAlignedText],
241
- children: agentName
242
- }), /*#__PURE__*/_jsx(Text, {
243
- style: [styles.leftAlignedText, styles.statusText],
244
- children: statusText
245
- })]
246
- }), /*#__PURE__*/_jsx(View, {
247
- style: styles.middleContentSection,
248
- children: /*#__PURE__*/_jsx(WaveformVisualizer, {
249
- roomRef: roomRef
250
- })
251
- }), /*#__PURE__*/_jsxs(View, {
252
- style: styles.rightContentSection,
253
- children: [!tokenDetails?.token && /*#__PURE__*/_jsx(View, {
254
- style: styles.buttonContainer,
255
- children: /*#__PURE__*/_jsx(TouchableOpacity, {
256
- onPress: handleStartCall,
257
- style: styles.startCallButton,
258
- children: /*#__PURE__*/_jsx(Text, {
259
- style: styles.startCallText,
260
- children: connectButtonText
261
- })
340
+ style: styles.callContentArea,
341
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
342
+ onPress: () => clearDataReceived(),
343
+ style: styles.closeButton,
344
+ accessibilityLabel: "Close",
345
+ accessibilityRole: "button",
346
+ children: /*#__PURE__*/_jsx(Image, {
347
+ source: {
348
+ uri: ICON_URLS.CLOSE_ICON
349
+ },
350
+ style: styles.popupCloseIcon
262
351
  })
263
- }), tokenDetails?.token && /*#__PURE__*/_jsxs(View, {
264
- style: styles.buttonContainer,
265
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
266
- style: styles.muteButton,
267
- onPress: handleMicToggle,
268
- children: /*#__PURE__*/_jsx(Image, {
269
- source: {
270
- uri: isMicMuted ? ICON_URLS.MIC_OFF : ICON_URLS.MIC_ON
352
+ }), livekitData.type === 'calculation' && /*#__PURE__*/_jsx(View, {
353
+ style: styles.callContentTextContainer,
354
+ children: /*#__PURE__*/_jsx(ScrollView, {
355
+ ref: calculationScrollRef,
356
+ style: styles.callContentScrollView,
357
+ contentContainerStyle: styles.callContentScrollViewContent,
358
+ showsVerticalScrollIndicator: true,
359
+ bounces: false,
360
+ nestedScrollEnabled: true,
361
+ onScroll: e => {
362
+ const {
363
+ contentOffset,
364
+ contentSize,
365
+ layoutMeasurement
366
+ } = e.nativeEvent;
367
+ const isAtBottom = contentOffset.y + layoutMeasurement.height >= contentSize.height - SCROLL_END_THRESHOLD;
368
+ userScrolledUpRef.current = !isAtBottom;
369
+ },
370
+ onContentSizeChange: () => {
371
+ if (!userScrolledUpRef.current) {
372
+ calculationScrollRef.current?.scrollToEnd({
373
+ animated: true
374
+ });
375
+ }
376
+ },
377
+ children: /*#__PURE__*/_jsx(Typewriter, {
378
+ lines: Array.isArray(livekitData.list) ? livekitData.list : [],
379
+ typingSpeed: 50,
380
+ textStyle: styles.callContentText,
381
+ showCursor: false,
382
+ onViewClose: flag => {
383
+ if (flag) {
384
+ clearDataReceived();
385
+ }
271
386
  },
272
- style: styles.buttonImage
387
+ viewCloseDelay: livekitData.delay || 15000
273
388
  })
274
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
275
- onPress: handleEndCall,
276
- style: styles.endCallButton,
277
- children: /*#__PURE__*/_jsx(Image, {
278
- source: {
279
- uri: ICON_URLS.END_CALL
280
- },
281
- style: styles.buttonImage
389
+ })
390
+ })]
391
+ }), /*#__PURE__*/_jsxs(View, {
392
+ style: styles.expandedContentContainer,
393
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
394
+ onPress: handleButtonPress,
395
+ style: styles.pressable,
396
+ children: configData?.agentAvatar?.avatarType === 'image' ? /*#__PURE__*/_jsx(Image, {
397
+ source: {
398
+ uri: configData?.agentAvatar?.avatarUrl
399
+ },
400
+ style: styles.iconImage
401
+ }) : /*#__PURE__*/_jsx(LottieView, {
402
+ ref: lottieRef,
403
+ source: lottieSource,
404
+ autoPlay: true,
405
+ loop: true,
406
+ style: styles.iconImage,
407
+ enableMergePathsAndroidForKitKatAndAbove: true,
408
+ enableSafeModeAndroid: true
409
+ })
410
+ }), /*#__PURE__*/_jsxs(View, {
411
+ style: styles.leftContentSection,
412
+ children: [/*#__PURE__*/_jsx(Text, {
413
+ style: [styles.agentNameText, styles.leftAlignedText],
414
+ numberOfLines: 1,
415
+ ellipsizeMode: "tail",
416
+ children: agentName
417
+ }), /*#__PURE__*/_jsx(Text, {
418
+ style: [styles.leftAlignedText, styles.statusText],
419
+ numberOfLines: 1,
420
+ ellipsizeMode: "tail",
421
+ children: statusText
422
+ })]
423
+ }), /*#__PURE__*/_jsx(View, {
424
+ style: styles.middleContentSection,
425
+ children: /*#__PURE__*/_jsx(WaveformVisualizer, {
426
+ roomRef: roomRef
427
+ })
428
+ }), /*#__PURE__*/_jsx(View, {
429
+ style: [styles.rightContentSection, styles.rightContentSectionPadded],
430
+ children: /*#__PURE__*/_jsx(View, {
431
+ style: styles.buttonContainer,
432
+ children: !isConnected ? /*#__PURE__*/_jsx(TouchableOpacity, {
433
+ onPress: handleStartCall,
434
+ style: styles.startCallButton,
435
+ accessibilityLabel: `Start call - ${connectButtonText}`,
436
+ accessibilityRole: "button",
437
+ children: /*#__PURE__*/_jsx(Text, {
438
+ style: styles.startCallText,
439
+ children: connectButtonText
440
+ })
441
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
442
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
443
+ style: styles.muteButton,
444
+ onPress: handleMicToggle,
445
+ accessibilityLabel: isMicMuted ? 'Unmute microphone' : 'Mute microphone',
446
+ accessibilityRole: "button",
447
+ children: /*#__PURE__*/_jsx(Image, {
448
+ source: micIconSource,
449
+ style: styles.buttonImage
450
+ })
451
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
452
+ onPress: handleEndCall,
453
+ style: styles.endCallButton,
454
+ accessibilityLabel: "End call",
455
+ accessibilityRole: "button",
456
+ children: /*#__PURE__*/_jsx(Image, {
457
+ source: {
458
+ uri: ICON_URLS.END_CALL
459
+ },
460
+ style: styles.buttonImage
461
+ })
462
+ })]
282
463
  })
464
+ })
465
+ })]
466
+ })]
467
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
468
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
469
+ onPress: handleButtonPress,
470
+ style: styles.pressable,
471
+ children: /*#__PURE__*/_jsx(LottieView, {
472
+ ref: lottieRef,
473
+ source: lottieSource,
474
+ autoPlay: true,
475
+ loop: true,
476
+ style: styles.iconImage,
477
+ enableMergePathsAndroidForKitKatAndAbove: true,
478
+ enableSafeModeAndroid: true
479
+ })
480
+ }), isOpen && /*#__PURE__*/_jsxs(Animated.View, {
481
+ style: [styles.expandedContentContainer, expandedContentAnimatedStyles],
482
+ children: [/*#__PURE__*/_jsxs(View, {
483
+ style: styles.leftContentSection,
484
+ children: [/*#__PURE__*/_jsx(Text, {
485
+ style: [styles.agentNameText, styles.leftAlignedText],
486
+ children: agentName
487
+ }), /*#__PURE__*/_jsx(Text, {
488
+ style: [styles.leftAlignedText, styles.statusText],
489
+ children: statusText
283
490
  })]
491
+ }), /*#__PURE__*/_jsx(View, {
492
+ style: styles.middleContentSection,
493
+ children: /*#__PURE__*/_jsx(WaveformVisualizer, {
494
+ roomRef: roomRef
495
+ })
496
+ }), /*#__PURE__*/_jsx(View, {
497
+ style: styles.rightContentSection,
498
+ children: /*#__PURE__*/_jsx(View, {
499
+ style: styles.buttonContainer,
500
+ children: !isConnected ? /*#__PURE__*/_jsx(TouchableOpacity, {
501
+ onPress: handleStartCall,
502
+ style: styles.startCallButton,
503
+ accessibilityLabel: `Start call - ${connectButtonText}`,
504
+ accessibilityRole: "button",
505
+ children: /*#__PURE__*/_jsx(Text, {
506
+ style: styles.startCallText,
507
+ children: connectButtonText
508
+ })
509
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
510
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
511
+ style: styles.muteButton,
512
+ onPress: handleMicToggle,
513
+ accessibilityLabel: isMicMuted ? 'Unmute microphone' : 'Mute microphone',
514
+ accessibilityRole: "button",
515
+ children: /*#__PURE__*/_jsx(Image, {
516
+ source: micIconSource,
517
+ style: styles.buttonImage
518
+ })
519
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
520
+ onPress: handleEndCall,
521
+ style: styles.endCallButton,
522
+ accessibilityLabel: "End call",
523
+ accessibilityRole: "button",
524
+ children: /*#__PURE__*/_jsx(Image, {
525
+ source: {
526
+ uri: ICON_URLS.END_CALL
527
+ },
528
+ style: styles.buttonImage
529
+ })
530
+ })]
531
+ })
532
+ })
284
533
  })]
285
534
  })]
286
535
  })]