@mobileai/react-native 0.9.17 → 0.9.19

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 (292) hide show
  1. package/LICENSE +28 -20
  2. package/MobileAIFloatingOverlay.podspec +25 -0
  3. package/android/build.gradle +61 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/com/mobileai/overlay/FloatingOverlayView.kt +151 -0
  6. package/android/src/main/java/com/mobileai/overlay/MobileAIOverlayPackage.kt +23 -0
  7. package/android/src/newarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +45 -0
  8. package/android/src/oldarch/com/mobileai/overlay/FloatingOverlayViewManager.kt +29 -0
  9. package/ios/MobileAIFloatingOverlayComponentView.mm +73 -0
  10. package/lib/module/components/AIAgent.js +902 -136
  11. package/lib/module/components/AIConsentDialog.js +439 -0
  12. package/lib/module/components/AgentChatBar.js +828 -134
  13. package/lib/module/components/AgentOverlay.js +2 -1
  14. package/lib/module/components/DiscoveryTooltip.js +21 -9
  15. package/lib/module/components/FloatingOverlayWrapper.js +108 -0
  16. package/lib/module/components/Icons.js +123 -0
  17. package/lib/module/config/endpoints.js +12 -2
  18. package/lib/module/core/AgentRuntime.js +373 -27
  19. package/lib/module/core/FiberAdapter.js +56 -0
  20. package/lib/module/core/FiberTreeWalker.js +186 -80
  21. package/lib/module/core/IdleDetector.js +19 -0
  22. package/lib/module/core/NativeAlertInterceptor.js +191 -0
  23. package/lib/module/core/systemPrompt.js +203 -45
  24. package/lib/module/index.js +3 -0
  25. package/lib/module/providers/GeminiProvider.js +72 -56
  26. package/lib/module/providers/ProviderFactory.js +6 -2
  27. package/lib/module/services/AudioInputService.js +3 -12
  28. package/lib/module/services/AudioOutputService.js +1 -13
  29. package/lib/module/services/ConversationService.js +166 -0
  30. package/lib/module/services/MobileAIKnowledgeRetriever.js +41 -0
  31. package/lib/module/services/VoiceService.js +29 -8
  32. package/lib/module/services/telemetry/MobileAI.js +44 -0
  33. package/lib/module/services/telemetry/TelemetryService.js +13 -1
  34. package/lib/module/services/telemetry/TouchAutoCapture.js +44 -18
  35. package/lib/module/specs/FloatingOverlayNativeComponent.ts +19 -0
  36. package/lib/module/support/CSATSurvey.js +95 -12
  37. package/lib/module/support/EscalationSocket.js +70 -1
  38. package/lib/module/support/ReportedIssueEventSource.js +148 -0
  39. package/lib/module/support/escalateTool.js +4 -2
  40. package/lib/module/support/index.js +1 -0
  41. package/lib/module/support/reportIssueTool.js +127 -0
  42. package/lib/module/support/supportPrompt.js +77 -9
  43. package/lib/module/tools/guideTool.js +2 -1
  44. package/lib/module/tools/longPressTool.js +4 -3
  45. package/lib/module/tools/pickerTool.js +6 -4
  46. package/lib/module/tools/tapTool.js +12 -3
  47. package/lib/module/tools/typeTool.js +19 -10
  48. package/lib/module/utils/logger.js +175 -6
  49. package/lib/typescript/react-native.config.d.ts +11 -0
  50. package/lib/typescript/src/components/AIAgent.d.ts +28 -2
  51. package/lib/typescript/src/components/AIConsentDialog.d.ts +153 -0
  52. package/lib/typescript/src/components/AgentChatBar.d.ts +15 -2
  53. package/lib/typescript/src/components/DiscoveryTooltip.d.ts +3 -1
  54. package/lib/typescript/src/components/FloatingOverlayWrapper.d.ts +51 -0
  55. package/lib/typescript/src/components/Icons.d.ts +8 -0
  56. package/lib/typescript/src/config/endpoints.d.ts +5 -3
  57. package/lib/typescript/src/core/AgentRuntime.d.ts +4 -0
  58. package/lib/typescript/src/core/FiberAdapter.d.ts +25 -0
  59. package/lib/typescript/src/core/FiberTreeWalker.d.ts +2 -0
  60. package/lib/typescript/src/core/IdleDetector.d.ts +11 -0
  61. package/lib/typescript/src/core/NativeAlertInterceptor.d.ts +55 -0
  62. package/lib/typescript/src/core/types.d.ts +106 -1
  63. package/lib/typescript/src/index.d.ts +9 -4
  64. package/lib/typescript/src/providers/GeminiProvider.d.ts +6 -5
  65. package/lib/typescript/src/services/ConversationService.d.ts +55 -0
  66. package/lib/typescript/src/services/MobileAIKnowledgeRetriever.d.ts +9 -0
  67. package/lib/typescript/src/services/telemetry/MobileAI.d.ts +7 -0
  68. package/lib/typescript/src/services/telemetry/TelemetryService.d.ts +1 -1
  69. package/lib/typescript/src/services/telemetry/TouchAutoCapture.d.ts +9 -6
  70. package/lib/typescript/src/services/telemetry/types.d.ts +3 -1
  71. package/lib/typescript/src/specs/FloatingOverlayNativeComponent.d.ts +17 -0
  72. package/lib/typescript/src/support/EscalationSocket.d.ts +17 -0
  73. package/lib/typescript/src/support/ReportedIssueEventSource.d.ts +24 -0
  74. package/lib/typescript/src/support/escalateTool.d.ts +5 -0
  75. package/lib/typescript/src/support/index.d.ts +2 -1
  76. package/lib/typescript/src/support/reportIssueTool.d.ts +20 -0
  77. package/lib/typescript/src/support/types.d.ts +56 -1
  78. package/lib/typescript/src/utils/logger.d.ts +15 -0
  79. package/package.json +20 -9
  80. package/react-native.config.js +12 -0
  81. package/lib/module/__cli_tmp__.js.map +0 -1
  82. package/lib/module/components/AIAgent.js.map +0 -1
  83. package/lib/module/components/AIZone.js.map +0 -1
  84. package/lib/module/components/AgentChatBar.js.map +0 -1
  85. package/lib/module/components/AgentErrorBoundary.js.map +0 -1
  86. package/lib/module/components/AgentOverlay.js.map +0 -1
  87. package/lib/module/components/DiscoveryTooltip.js.map +0 -1
  88. package/lib/module/components/HighlightOverlay.js.map +0 -1
  89. package/lib/module/components/Icons.js.map +0 -1
  90. package/lib/module/components/ProactiveHint.js.map +0 -1
  91. package/lib/module/components/cards/InfoCard.js.map +0 -1
  92. package/lib/module/components/cards/ReviewSummary.js.map +0 -1
  93. package/lib/module/config/endpoints.js.map +0 -1
  94. package/lib/module/core/ActionRegistry.js.map +0 -1
  95. package/lib/module/core/AgentRuntime.js.map +0 -1
  96. package/lib/module/core/FiberTreeWalker.js.map +0 -1
  97. package/lib/module/core/IdleDetector.js.map +0 -1
  98. package/lib/module/core/MCPBridge.js.map +0 -1
  99. package/lib/module/core/ScreenDehydrator.js.map +0 -1
  100. package/lib/module/core/ZoneRegistry.js.map +0 -1
  101. package/lib/module/core/systemPrompt.js.map +0 -1
  102. package/lib/module/core/types.js.map +0 -1
  103. package/lib/module/hooks/useAction.js.map +0 -1
  104. package/lib/module/index.js.map +0 -1
  105. package/lib/module/plugin/withAppIntents.js.map +0 -1
  106. package/lib/module/providers/GeminiProvider.js.map +0 -1
  107. package/lib/module/providers/OpenAIProvider.js.map +0 -1
  108. package/lib/module/providers/ProviderFactory.js.map +0 -1
  109. package/lib/module/services/AudioInputService.js.map +0 -1
  110. package/lib/module/services/AudioOutputService.js.map +0 -1
  111. package/lib/module/services/KnowledgeBaseService.js.map +0 -1
  112. package/lib/module/services/VoiceService.js.map +0 -1
  113. package/lib/module/services/flags/FlagService.js.map +0 -1
  114. package/lib/module/services/telemetry/MobileAI.js.map +0 -1
  115. package/lib/module/services/telemetry/PiiScrubber.js.map +0 -1
  116. package/lib/module/services/telemetry/TelemetryService.js.map +0 -1
  117. package/lib/module/services/telemetry/TouchAutoCapture.js.map +0 -1
  118. package/lib/module/services/telemetry/device.js.map +0 -1
  119. package/lib/module/services/telemetry/deviceMetadata.js.map +0 -1
  120. package/lib/module/services/telemetry/index.js.map +0 -1
  121. package/lib/module/services/telemetry/types.js.map +0 -1
  122. package/lib/module/support/CSATSurvey.js.map +0 -1
  123. package/lib/module/support/EscalationEventSource.js.map +0 -1
  124. package/lib/module/support/EscalationSocket.js.map +0 -1
  125. package/lib/module/support/SupportChatModal.js.map +0 -1
  126. package/lib/module/support/SupportGreeting.js.map +0 -1
  127. package/lib/module/support/TicketStore.js.map +0 -1
  128. package/lib/module/support/escalateTool.js.map +0 -1
  129. package/lib/module/support/index.js.map +0 -1
  130. package/lib/module/support/supportPrompt.js.map +0 -1
  131. package/lib/module/support/types.js.map +0 -1
  132. package/lib/module/tools/datePickerTool.js.map +0 -1
  133. package/lib/module/tools/guideTool.js.map +0 -1
  134. package/lib/module/tools/index.js.map +0 -1
  135. package/lib/module/tools/keyboardTool.js.map +0 -1
  136. package/lib/module/tools/longPressTool.js.map +0 -1
  137. package/lib/module/tools/pickerTool.js.map +0 -1
  138. package/lib/module/tools/restoreTool.js.map +0 -1
  139. package/lib/module/tools/scrollTool.js.map +0 -1
  140. package/lib/module/tools/simplifyTool.js.map +0 -1
  141. package/lib/module/tools/sliderTool.js.map +0 -1
  142. package/lib/module/tools/tapTool.js.map +0 -1
  143. package/lib/module/tools/typeTool.js.map +0 -1
  144. package/lib/module/tools/types.js.map +0 -1
  145. package/lib/module/types/jsx.d.js.map +0 -1
  146. package/lib/module/utils/audioUtils.js.map +0 -1
  147. package/lib/module/utils/logger.js.map +0 -1
  148. package/lib/typescript/babel.config.d.ts.map +0 -1
  149. package/lib/typescript/bin/generate-map.d.cts.map +0 -1
  150. package/lib/typescript/eslint.config.d.mts.map +0 -1
  151. package/lib/typescript/generate-map.d.ts.map +0 -1
  152. package/lib/typescript/src/__cli_tmp__.d.ts.map +0 -1
  153. package/lib/typescript/src/components/AIAgent.d.ts.map +0 -1
  154. package/lib/typescript/src/components/AIZone.d.ts.map +0 -1
  155. package/lib/typescript/src/components/AgentChatBar.d.ts.map +0 -1
  156. package/lib/typescript/src/components/AgentErrorBoundary.d.ts.map +0 -1
  157. package/lib/typescript/src/components/AgentOverlay.d.ts.map +0 -1
  158. package/lib/typescript/src/components/DiscoveryTooltip.d.ts.map +0 -1
  159. package/lib/typescript/src/components/HighlightOverlay.d.ts.map +0 -1
  160. package/lib/typescript/src/components/Icons.d.ts.map +0 -1
  161. package/lib/typescript/src/components/ProactiveHint.d.ts.map +0 -1
  162. package/lib/typescript/src/components/cards/InfoCard.d.ts.map +0 -1
  163. package/lib/typescript/src/components/cards/ReviewSummary.d.ts.map +0 -1
  164. package/lib/typescript/src/config/endpoints.d.ts.map +0 -1
  165. package/lib/typescript/src/core/ActionRegistry.d.ts.map +0 -1
  166. package/lib/typescript/src/core/AgentRuntime.d.ts.map +0 -1
  167. package/lib/typescript/src/core/FiberTreeWalker.d.ts.map +0 -1
  168. package/lib/typescript/src/core/IdleDetector.d.ts.map +0 -1
  169. package/lib/typescript/src/core/MCPBridge.d.ts.map +0 -1
  170. package/lib/typescript/src/core/ScreenDehydrator.d.ts.map +0 -1
  171. package/lib/typescript/src/core/ZoneRegistry.d.ts.map +0 -1
  172. package/lib/typescript/src/core/systemPrompt.d.ts.map +0 -1
  173. package/lib/typescript/src/core/types.d.ts.map +0 -1
  174. package/lib/typescript/src/hooks/useAction.d.ts.map +0 -1
  175. package/lib/typescript/src/index.d.ts.map +0 -1
  176. package/lib/typescript/src/plugin/withAppIntents.d.ts.map +0 -1
  177. package/lib/typescript/src/providers/GeminiProvider.d.ts.map +0 -1
  178. package/lib/typescript/src/providers/OpenAIProvider.d.ts.map +0 -1
  179. package/lib/typescript/src/providers/ProviderFactory.d.ts.map +0 -1
  180. package/lib/typescript/src/services/AudioInputService.d.ts.map +0 -1
  181. package/lib/typescript/src/services/AudioOutputService.d.ts.map +0 -1
  182. package/lib/typescript/src/services/KnowledgeBaseService.d.ts.map +0 -1
  183. package/lib/typescript/src/services/VoiceService.d.ts.map +0 -1
  184. package/lib/typescript/src/services/flags/FlagService.d.ts.map +0 -1
  185. package/lib/typescript/src/services/telemetry/MobileAI.d.ts.map +0 -1
  186. package/lib/typescript/src/services/telemetry/PiiScrubber.d.ts.map +0 -1
  187. package/lib/typescript/src/services/telemetry/TelemetryService.d.ts.map +0 -1
  188. package/lib/typescript/src/services/telemetry/TouchAutoCapture.d.ts.map +0 -1
  189. package/lib/typescript/src/services/telemetry/device.d.ts.map +0 -1
  190. package/lib/typescript/src/services/telemetry/deviceMetadata.d.ts.map +0 -1
  191. package/lib/typescript/src/services/telemetry/index.d.ts.map +0 -1
  192. package/lib/typescript/src/services/telemetry/types.d.ts.map +0 -1
  193. package/lib/typescript/src/support/CSATSurvey.d.ts.map +0 -1
  194. package/lib/typescript/src/support/EscalationEventSource.d.ts.map +0 -1
  195. package/lib/typescript/src/support/EscalationSocket.d.ts.map +0 -1
  196. package/lib/typescript/src/support/SupportChatModal.d.ts.map +0 -1
  197. package/lib/typescript/src/support/SupportGreeting.d.ts.map +0 -1
  198. package/lib/typescript/src/support/TicketStore.d.ts.map +0 -1
  199. package/lib/typescript/src/support/escalateTool.d.ts.map +0 -1
  200. package/lib/typescript/src/support/index.d.ts.map +0 -1
  201. package/lib/typescript/src/support/supportPrompt.d.ts.map +0 -1
  202. package/lib/typescript/src/support/types.d.ts.map +0 -1
  203. package/lib/typescript/src/tools/datePickerTool.d.ts.map +0 -1
  204. package/lib/typescript/src/tools/guideTool.d.ts.map +0 -1
  205. package/lib/typescript/src/tools/index.d.ts.map +0 -1
  206. package/lib/typescript/src/tools/keyboardTool.d.ts.map +0 -1
  207. package/lib/typescript/src/tools/longPressTool.d.ts.map +0 -1
  208. package/lib/typescript/src/tools/pickerTool.d.ts.map +0 -1
  209. package/lib/typescript/src/tools/restoreTool.d.ts.map +0 -1
  210. package/lib/typescript/src/tools/scrollTool.d.ts.map +0 -1
  211. package/lib/typescript/src/tools/simplifyTool.d.ts.map +0 -1
  212. package/lib/typescript/src/tools/sliderTool.d.ts.map +0 -1
  213. package/lib/typescript/src/tools/tapTool.d.ts.map +0 -1
  214. package/lib/typescript/src/tools/typeTool.d.ts.map +0 -1
  215. package/lib/typescript/src/tools/types.d.ts.map +0 -1
  216. package/lib/typescript/src/utils/audioUtils.d.ts.map +0 -1
  217. package/lib/typescript/src/utils/logger.d.ts.map +0 -1
  218. package/src/__cli_tmp__.tsx +0 -9
  219. package/src/cli/analyzers/chain-analyzer.ts +0 -183
  220. package/src/cli/extractors/ai-extractor.ts +0 -6
  221. package/src/cli/extractors/ast-extractor.ts +0 -551
  222. package/src/cli/generate-intents.ts +0 -140
  223. package/src/cli/generate-map.ts +0 -121
  224. package/src/cli/generate-swift.ts +0 -116
  225. package/src/cli/scanners/expo-scanner.ts +0 -203
  226. package/src/cli/scanners/rn-scanner.ts +0 -445
  227. package/src/components/AIAgent.tsx +0 -1716
  228. package/src/components/AIZone.tsx +0 -147
  229. package/src/components/AgentChatBar.tsx +0 -1143
  230. package/src/components/AgentErrorBoundary.tsx +0 -78
  231. package/src/components/AgentOverlay.tsx +0 -73
  232. package/src/components/DiscoveryTooltip.tsx +0 -148
  233. package/src/components/HighlightOverlay.tsx +0 -136
  234. package/src/components/Icons.tsx +0 -253
  235. package/src/components/ProactiveHint.tsx +0 -145
  236. package/src/components/cards/InfoCard.tsx +0 -58
  237. package/src/components/cards/ReviewSummary.tsx +0 -76
  238. package/src/config/endpoints.ts +0 -22
  239. package/src/core/ActionRegistry.ts +0 -105
  240. package/src/core/AgentRuntime.ts +0 -1471
  241. package/src/core/FiberTreeWalker.ts +0 -930
  242. package/src/core/IdleDetector.ts +0 -72
  243. package/src/core/MCPBridge.ts +0 -163
  244. package/src/core/ScreenDehydrator.ts +0 -53
  245. package/src/core/ZoneRegistry.ts +0 -44
  246. package/src/core/systemPrompt.ts +0 -431
  247. package/src/core/types.ts +0 -521
  248. package/src/hooks/useAction.ts +0 -182
  249. package/src/index.ts +0 -83
  250. package/src/plugin/withAppIntents.ts +0 -98
  251. package/src/providers/GeminiProvider.ts +0 -357
  252. package/src/providers/OpenAIProvider.ts +0 -379
  253. package/src/providers/ProviderFactory.ts +0 -36
  254. package/src/services/AudioInputService.ts +0 -226
  255. package/src/services/AudioOutputService.ts +0 -236
  256. package/src/services/KnowledgeBaseService.ts +0 -156
  257. package/src/services/VoiceService.ts +0 -451
  258. package/src/services/flags/FlagService.ts +0 -137
  259. package/src/services/telemetry/MobileAI.ts +0 -66
  260. package/src/services/telemetry/PiiScrubber.ts +0 -17
  261. package/src/services/telemetry/TelemetryService.ts +0 -323
  262. package/src/services/telemetry/TouchAutoCapture.ts +0 -165
  263. package/src/services/telemetry/device.ts +0 -93
  264. package/src/services/telemetry/deviceMetadata.ts +0 -13
  265. package/src/services/telemetry/index.ts +0 -13
  266. package/src/services/telemetry/types.ts +0 -75
  267. package/src/support/CSATSurvey.tsx +0 -304
  268. package/src/support/EscalationEventSource.ts +0 -190
  269. package/src/support/EscalationSocket.ts +0 -152
  270. package/src/support/SupportChatModal.tsx +0 -563
  271. package/src/support/SupportGreeting.tsx +0 -161
  272. package/src/support/TicketStore.ts +0 -100
  273. package/src/support/escalateTool.ts +0 -174
  274. package/src/support/index.ts +0 -29
  275. package/src/support/supportPrompt.ts +0 -55
  276. package/src/support/types.ts +0 -155
  277. package/src/tools/datePickerTool.ts +0 -60
  278. package/src/tools/guideTool.ts +0 -76
  279. package/src/tools/index.ts +0 -20
  280. package/src/tools/keyboardTool.ts +0 -30
  281. package/src/tools/longPressTool.ts +0 -61
  282. package/src/tools/pickerTool.ts +0 -115
  283. package/src/tools/restoreTool.ts +0 -33
  284. package/src/tools/scrollTool.ts +0 -156
  285. package/src/tools/simplifyTool.ts +0 -33
  286. package/src/tools/sliderTool.ts +0 -65
  287. package/src/tools/tapTool.ts +0 -93
  288. package/src/tools/typeTool.ts +0 -113
  289. package/src/tools/types.ts +0 -58
  290. package/src/types/jsx.d.ts +0 -20
  291. package/src/utils/audioUtils.ts +0 -54
  292. package/src/utils/logger.ts +0 -38
@@ -1,304 +0,0 @@
1
- /**
2
- * CSAT Survey — Customer Satisfaction component.
3
- *
4
- * Shown after a support conversation ends (or after idle timeout).
5
- * Supports three rating types: emoji, stars, thumbs.
6
- */
7
-
8
-
9
- import { useState } from 'react';
10
- import {
11
- View,
12
- Text,
13
- TouchableOpacity,
14
- TextInput,
15
- StyleSheet,
16
- } from 'react-native';
17
- import type { CSATConfig, CSATRating } from './types';
18
-
19
- interface CSATSurveyProps {
20
- config: CSATConfig;
21
- metadata: CSATRating['metadata'];
22
- onDismiss: () => void;
23
- theme?: {
24
- primaryColor?: string;
25
- textColor?: string;
26
- backgroundColor?: string;
27
- };
28
- }
29
-
30
- const EMOJI_OPTIONS = [
31
- { emoji: '😡', label: 'Terrible', score: 1 },
32
- { emoji: '😞', label: 'Bad', score: 2 },
33
- { emoji: '😐', label: 'Okay', score: 3 },
34
- { emoji: '😊', label: 'Good', score: 4 },
35
- { emoji: '🤩', label: 'Amazing', score: 5 },
36
- ];
37
-
38
- const STAR_COUNT = 5;
39
-
40
- export function CSATSurvey({
41
- config,
42
- metadata,
43
- onDismiss,
44
- theme,
45
- }: CSATSurveyProps) {
46
- const [selectedScore, setSelectedScore] = useState<number | null>(null);
47
- const [feedback, setFeedback] = useState('');
48
- const [submitted, setSubmitted] = useState(false);
49
-
50
- const primary = theme?.primaryColor ?? '#8b5cf6';
51
- const textColor = theme?.textColor ?? '#ffffff';
52
- const bgColor = theme?.backgroundColor ?? 'rgba(26, 26, 46, 0.98)';
53
- const ratingType = config.ratingType ?? 'emoji';
54
- const question = config.question ?? 'How was your experience?';
55
-
56
- const handleSubmit = () => {
57
- if (selectedScore === null) return;
58
-
59
- const rating: CSATRating = {
60
- score: selectedScore,
61
- feedback: feedback.trim() || undefined,
62
- metadata,
63
- };
64
-
65
- config.onSubmit(rating);
66
- setSubmitted(true);
67
-
68
- // Auto-dismiss after 1.5s
69
- setTimeout(onDismiss, 1500);
70
- };
71
-
72
- if (submitted) {
73
- return (
74
- <View style={[styles.container, { backgroundColor: bgColor }]}>
75
- <Text style={[styles.thankYou, { color: textColor }]}>
76
- Thank you for your feedback! 🙏
77
- </Text>
78
- </View>
79
- );
80
- }
81
-
82
- return (
83
- <View style={[styles.container, { backgroundColor: bgColor }]}>
84
- {/* Question */}
85
- <Text style={[styles.question, { color: textColor }]}>{question}</Text>
86
-
87
- {/* Rating selector */}
88
- <View style={styles.ratingContainer}>
89
- {ratingType === 'emoji' && (
90
- <View style={styles.emojiRow}>
91
- {EMOJI_OPTIONS.map((opt) => (
92
- <TouchableOpacity
93
- key={opt.score}
94
- onPress={() => setSelectedScore(opt.score)}
95
- style={[
96
- styles.emojiButton,
97
- selectedScore === opt.score && {
98
- backgroundColor: `${primary}30`,
99
- borderColor: primary,
100
- },
101
- ]}
102
- activeOpacity={0.7}
103
- >
104
- <Text style={styles.emoji}>{opt.emoji}</Text>
105
- <Text
106
- style={[
107
- styles.emojiLabel,
108
- { color: selectedScore === opt.score ? primary : '#71717a' },
109
- ]}
110
- >
111
- {opt.label}
112
- </Text>
113
- </TouchableOpacity>
114
- ))}
115
- </View>
116
- )}
117
-
118
- {ratingType === 'stars' && (
119
- <View style={styles.starsRow}>
120
- {Array.from({ length: STAR_COUNT }, (_, i) => i + 1).map(
121
- (star) => (
122
- <TouchableOpacity
123
- key={star}
124
- onPress={() => setSelectedScore(star)}
125
- activeOpacity={0.7}
126
- >
127
- <Text
128
- style={[
129
- styles.star,
130
- {
131
- color:
132
- selectedScore !== null && star <= selectedScore
133
- ? '#fbbf24'
134
- : '#52525b',
135
- },
136
- ]}
137
- >
138
-
139
- </Text>
140
- </TouchableOpacity>
141
- )
142
- )}
143
- </View>
144
- )}
145
-
146
- {ratingType === 'thumbs' && (
147
- <View style={styles.thumbsRow}>
148
- <TouchableOpacity
149
- onPress={() => setSelectedScore(0)}
150
- style={[
151
- styles.thumbButton,
152
- selectedScore === 0 && {
153
- backgroundColor: '#ef444430',
154
- borderColor: '#ef4444',
155
- },
156
- ]}
157
- activeOpacity={0.7}
158
- >
159
- <Text style={styles.thumbEmoji}>👎</Text>
160
- </TouchableOpacity>
161
- <TouchableOpacity
162
- onPress={() => setSelectedScore(1)}
163
- style={[
164
- styles.thumbButton,
165
- selectedScore === 1 && {
166
- backgroundColor: '#22c55e30',
167
- borderColor: '#22c55e',
168
- },
169
- ]}
170
- activeOpacity={0.7}
171
- >
172
- <Text style={styles.thumbEmoji}>👍</Text>
173
- </TouchableOpacity>
174
- </View>
175
- )}
176
- </View>
177
-
178
- {/* Optional feedback text */}
179
- {selectedScore !== null && (
180
- <TextInput
181
- style={[styles.feedbackInput, { color: textColor }]}
182
- placeholder="Any additional feedback? (optional)"
183
- placeholderTextColor="#52525b"
184
- value={feedback}
185
- onChangeText={setFeedback}
186
- multiline
187
- maxLength={500}
188
- />
189
- )}
190
-
191
- {/* Actions */}
192
- <View style={styles.actions}>
193
- <TouchableOpacity onPress={onDismiss} activeOpacity={0.7}>
194
- <Text style={styles.dismissText}>Skip</Text>
195
- </TouchableOpacity>
196
- {selectedScore !== null && (
197
- <TouchableOpacity
198
- style={[styles.submitButton, { backgroundColor: primary }]}
199
- onPress={handleSubmit}
200
- activeOpacity={0.7}
201
- >
202
- <Text style={[styles.submitText, { color: textColor }]}>
203
- Submit
204
- </Text>
205
- </TouchableOpacity>
206
- )}
207
- </View>
208
- </View>
209
- );
210
- }
211
-
212
- const styles = StyleSheet.create({
213
- container: {
214
- borderRadius: 16,
215
- padding: 20,
216
- margin: 12,
217
- },
218
- question: {
219
- fontSize: 16,
220
- fontWeight: '600',
221
- textAlign: 'center',
222
- marginBottom: 16,
223
- },
224
- thankYou: {
225
- fontSize: 16,
226
- fontWeight: '500',
227
- textAlign: 'center',
228
- paddingVertical: 12,
229
- },
230
- ratingContainer: {
231
- marginBottom: 12,
232
- },
233
- emojiRow: {
234
- flexDirection: 'row',
235
- justifyContent: 'center',
236
- gap: 8,
237
- },
238
- emojiButton: {
239
- alignItems: 'center',
240
- paddingHorizontal: 10,
241
- paddingVertical: 8,
242
- borderRadius: 12,
243
- borderWidth: 1,
244
- borderColor: 'transparent',
245
- },
246
- emoji: {
247
- fontSize: 28,
248
- },
249
- emojiLabel: {
250
- fontSize: 10,
251
- marginTop: 4,
252
- fontWeight: '500',
253
- },
254
- starsRow: {
255
- flexDirection: 'row',
256
- justifyContent: 'center',
257
- gap: 8,
258
- },
259
- star: {
260
- fontSize: 36,
261
- },
262
- thumbsRow: {
263
- flexDirection: 'row',
264
- justifyContent: 'center',
265
- gap: 20,
266
- },
267
- thumbButton: {
268
- padding: 12,
269
- borderRadius: 16,
270
- borderWidth: 1,
271
- borderColor: 'transparent',
272
- },
273
- thumbEmoji: {
274
- fontSize: 36,
275
- },
276
- feedbackInput: {
277
- borderWidth: 1,
278
- borderColor: '#3f3f46',
279
- borderRadius: 12,
280
- padding: 12,
281
- fontSize: 14,
282
- minHeight: 60,
283
- textAlignVertical: 'top',
284
- marginBottom: 12,
285
- },
286
- actions: {
287
- flexDirection: 'row',
288
- justifyContent: 'space-between',
289
- alignItems: 'center',
290
- },
291
- dismissText: {
292
- color: '#71717a',
293
- fontSize: 14,
294
- },
295
- submitButton: {
296
- paddingHorizontal: 20,
297
- paddingVertical: 10,
298
- borderRadius: 10,
299
- },
300
- submitText: {
301
- fontSize: 14,
302
- fontWeight: '600',
303
- },
304
- });
@@ -1,190 +0,0 @@
1
- /**
2
- * EscalationEventSource — SSE client using fetch + ReadableStream.
3
- *
4
- * Uses only the fetch API (available in all React Native runtimes)
5
- * to consume Server-Sent Events — no EventSource polyfill needed.
6
- * Provides a reliable, auto-reconnecting channel for server-push
7
- * events like `ticket_closed` that complements the bidirectional
8
- * WebSocket used for chat.
9
- *
10
- * Lifecycle:
11
- * 1. SDK calls connect() → fetch with streaming response
12
- * 2. Server holds connection open, pushes `ticket_closed` when agent resolves
13
- * 3. On disconnect, auto-reconnects with exponential backoff (max 5 attempts)
14
- * 4. If ticket is already closed, server responds immediately with the event
15
- */
16
-
17
- import { logger } from '../utils/logger';
18
-
19
- export interface EscalationEventSourceOptions {
20
- url: string;
21
- onTicketClosed?: (ticketId: string) => void;
22
- onConnected?: (ticketId: string) => void;
23
- onError?: (error: Error) => void;
24
- }
25
-
26
- export class EscalationEventSource {
27
- private abortController: AbortController | null = null;
28
- private intentionalClose = false;
29
- private reconnectAttempts = 0;
30
- private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
31
- private readonly maxReconnectAttempts = 5;
32
- private readonly options: EscalationEventSourceOptions;
33
-
34
- constructor(options: EscalationEventSourceOptions) {
35
- this.options = options;
36
- }
37
-
38
- connect(): void {
39
- this.intentionalClose = false;
40
- this.reconnectAttempts = 0;
41
- this.openConnection();
42
- }
43
-
44
- disconnect(): void {
45
- this.intentionalClose = true;
46
- if (this.reconnectTimer) {
47
- clearTimeout(this.reconnectTimer);
48
- this.reconnectTimer = null;
49
- }
50
- if (this.abortController) {
51
- this.abortController.abort();
52
- this.abortController = null;
53
- }
54
- }
55
-
56
- private async openConnection(): Promise<void> {
57
- if (this.intentionalClose) return;
58
-
59
- this.abortController = new AbortController();
60
-
61
- try {
62
- const response = await fetch(this.options.url, {
63
- signal: this.abortController.signal,
64
- headers: { Accept: 'text/event-stream' },
65
- });
66
-
67
- if (!response.ok) {
68
- logger.warn('EscalationSSE', 'Non-OK response:', response.status);
69
- this.scheduleReconnect();
70
- return;
71
- }
72
-
73
- if (!response.body) {
74
- logger.warn('EscalationSSE', 'No readable body — falling back to reading full response');
75
- await this.readFullResponse(response);
76
- return;
77
- }
78
-
79
- this.reconnectAttempts = 0;
80
- await this.readStream(response.body);
81
- } catch (err) {
82
- if (this.intentionalClose) return;
83
- if ((err as Error).name === 'AbortError') return;
84
- logger.warn('EscalationSSE', 'Connection error:', (err as Error).message);
85
- this.options.onError?.(err as Error);
86
- this.scheduleReconnect();
87
- }
88
- }
89
-
90
- private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {
91
- const reader = body.getReader();
92
- const decoder = new TextDecoder();
93
- let buffer = '';
94
-
95
- try {
96
- while (true) {
97
- const { done, value } = await reader.read();
98
- if (done) break;
99
-
100
- buffer += decoder.decode(value, { stream: true });
101
- const lines = buffer.split('\n');
102
- buffer = lines.pop()!;
103
-
104
- let currentEvent = '';
105
- let currentData = '';
106
-
107
- for (const line of lines) {
108
- if (line.startsWith('event: ')) {
109
- currentEvent = line.slice(7).trim();
110
- } else if (line.startsWith('data: ')) {
111
- currentData = line.slice(6).trim();
112
- } else if (line === '' && currentEvent && currentData) {
113
- this.handleEvent(currentEvent, currentData);
114
- currentEvent = '';
115
- currentData = '';
116
- }
117
- }
118
- }
119
- } catch (err) {
120
- if (this.intentionalClose) return;
121
- if ((err as Error).name === 'AbortError') return;
122
- logger.warn('EscalationSSE', 'Stream read error:', (err as Error).message);
123
- }
124
-
125
- if (!this.intentionalClose) {
126
- this.scheduleReconnect();
127
- }
128
- }
129
-
130
- private async readFullResponse(response: Response): Promise<void> {
131
- try {
132
- const text = await response.text();
133
- let currentEvent = '';
134
- let currentData = '';
135
-
136
- for (const line of text.split('\n')) {
137
- if (line.startsWith('event: ')) {
138
- currentEvent = line.slice(7).trim();
139
- } else if (line.startsWith('data: ')) {
140
- currentData = line.slice(6).trim();
141
- } else if (line === '' && currentEvent && currentData) {
142
- this.handleEvent(currentEvent, currentData);
143
- currentEvent = '';
144
- currentData = '';
145
- }
146
- }
147
- } catch (err) {
148
- if (this.intentionalClose) return;
149
- logger.warn('EscalationSSE', 'Full response read error:', (err as Error).message);
150
- }
151
-
152
- if (!this.intentionalClose) {
153
- this.scheduleReconnect();
154
- }
155
- }
156
-
157
- private handleEvent(event: string, data: string): void {
158
- try {
159
- const parsed = JSON.parse(data);
160
-
161
- if (event === 'connected') {
162
- logger.info('EscalationSSE', 'Connected for ticket:', parsed.ticketId);
163
- this.options.onConnected?.(parsed.ticketId);
164
- } else if (event === 'ticket_closed') {
165
- logger.info('EscalationSSE', 'Ticket closed event:', parsed.ticketId);
166
- this.options.onTicketClosed?.(parsed.ticketId);
167
- this.intentionalClose = true;
168
- this.abortController?.abort();
169
- }
170
- } catch {
171
- // ignore parse error
172
- }
173
- }
174
-
175
- private scheduleReconnect(): void {
176
- if (this.intentionalClose) return;
177
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
178
- logger.warn('EscalationSSE', 'Max reconnect attempts reached — giving up');
179
- return;
180
- }
181
-
182
- const delay = Math.min(1000 * 2 ** this.reconnectAttempts, 16_000);
183
- this.reconnectAttempts++;
184
- logger.info('EscalationSSE', `Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
185
-
186
- this.reconnectTimer = setTimeout(() => {
187
- this.openConnection();
188
- }, delay);
189
- }
190
- }
@@ -1,152 +0,0 @@
1
- /**
2
- * EscalationSocket — manages a WebSocket connection to the MobileAI platform
3
- * for receiving real-time replies from human support agents.
4
- *
5
- * Lifecycle:
6
- * 1. SDK calls escalate_to_human → POST /api/v1/escalations → gets { ticketId, wsUrl }
7
- * 2. EscalationSocket.connect(wsUrl) opens a WS connection
8
- * 3. Platform pushes { type: 'reply', ticketId, reply } when agent responds
9
- * 4. onReply callback fires → shown in chat UI as "👤 Human Agent: <reply>"
10
- * 5. disconnect() on chat close / unmount
11
- *
12
- * Handles:
13
- * - Server heartbeat pings (type: 'ping') — acknowledged silently
14
- * - Auto-reconnect on unexpected close (max 3 attempts, exponential backoff)
15
- */
16
-
17
- export type SocketReplyHandler = (reply: string, ticketId?: string) => void;
18
-
19
- interface EscalationSocketOptions {
20
- onReply: SocketReplyHandler;
21
- onError?: (error: Event) => void;
22
- onTypingChange?: (isTyping: boolean) => void;
23
- onTicketClosed?: (ticketId?: string) => void;
24
- maxReconnectAttempts?: number;
25
- }
26
-
27
- export class EscalationSocket {
28
- private ws: WebSocket | null = null;
29
- private wsUrl: string | null = null;
30
- private reconnectAttempts = 0;
31
- private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
32
- private intentionalClose = false;
33
-
34
- private readonly onReply: SocketReplyHandler;
35
- private readonly onError?: (error: Event) => void;
36
- private readonly onTypingChange?: (isTyping: boolean) => void;
37
- private readonly onTicketClosed?: (ticketId?: string) => void;
38
- private readonly maxReconnectAttempts: number;
39
-
40
- constructor(options: EscalationSocketOptions) {
41
- this.onReply = options.onReply;
42
- this.onError = options.onError;
43
- this.onTypingChange = options.onTypingChange;
44
- this.onTicketClosed = options.onTicketClosed;
45
- this.maxReconnectAttempts = options.maxReconnectAttempts ?? 3;
46
- }
47
-
48
- connect(wsUrl: string): void {
49
- this.wsUrl = wsUrl;
50
- this.intentionalClose = false;
51
- this.openConnection();
52
- }
53
-
54
- sendText(text: string): boolean {
55
- if (this.ws?.readyState === 1) { // WebSocket.OPEN
56
- this.ws.send(JSON.stringify({ type: 'user_message', content: text }));
57
- return true;
58
- }
59
- return false;
60
- }
61
-
62
- sendTypingStatus(isTyping: boolean): boolean {
63
- if (this.ws?.readyState === 1) {
64
- this.ws.send(JSON.stringify({ type: isTyping ? 'typing_start' : 'typing_stop' }));
65
- return true;
66
- }
67
- return false;
68
- }
69
-
70
- disconnect(): void {
71
- this.intentionalClose = true;
72
- if (this.reconnectTimer) {
73
- clearTimeout(this.reconnectTimer);
74
- this.reconnectTimer = null;
75
- }
76
- if (this.ws) {
77
- this.ws.close();
78
- this.ws = null;
79
- }
80
- }
81
-
82
- private openConnection(): void {
83
- if (!this.wsUrl) return;
84
-
85
- try {
86
- this.ws = new WebSocket(this.wsUrl);
87
- } catch (err) {
88
- console.error('[EscalationSocket] Failed to open WebSocket:', err);
89
- return;
90
- }
91
-
92
- this.ws.onopen = () => {
93
- console.log('[EscalationSocket] ✅ Connected to:', this.wsUrl);
94
- this.reconnectAttempts = 0;
95
- };
96
-
97
- this.ws.onmessage = (event) => {
98
- try {
99
- const rawData = String(event.data);
100
- console.log('[EscalationSocket] Message received:', rawData);
101
- const msg = JSON.parse(rawData);
102
- if (msg.type === 'ping') {
103
- console.log('[EscalationSocket] Heartbeat ping received');
104
- return;
105
- }
106
- if (msg.type === 'reply' && msg.reply) {
107
- console.log('[EscalationSocket] Human reply received:', msg.reply);
108
- this.onTypingChange?.(false);
109
- this.onReply(msg.reply, msg.ticketId);
110
- } else if (msg.type === 'typing_start') {
111
- this.onTypingChange?.(true);
112
- } else if (msg.type === 'typing_stop') {
113
- this.onTypingChange?.(false);
114
- } else if (msg.type === 'ticket_closed') {
115
- console.log('[EscalationSocket] Ticket closed by agent');
116
- this.onTypingChange?.(false);
117
- this.onTicketClosed?.(msg.ticketId);
118
- this.intentionalClose = true;
119
- this.ws?.close();
120
- }
121
- } catch {
122
- // Non-JSON message — ignore
123
- }
124
- };
125
-
126
- this.ws.onerror = (event) => {
127
- console.error('[EscalationSocket] ❌ WebSocket error. URL was:', this.wsUrl, event);
128
- this.onError?.(event);
129
- };
130
-
131
- this.ws.onclose = (event) => {
132
- console.warn(`[EscalationSocket] Connection closed. Code=${event.code} Reason="${event.reason}" Intentional=${this.intentionalClose}`);
133
- if (this.intentionalClose) return;
134
- this.scheduleReconnect();
135
- };
136
- }
137
-
138
- private scheduleReconnect(): void {
139
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
140
- console.warn('[EscalationSocket] Max reconnect attempts reached — giving up');
141
- return;
142
- }
143
- const delay = Math.min(1000 * 2 ** this.reconnectAttempts, 16_000);
144
- this.reconnectAttempts++;
145
- console.log(
146
- `[EscalationSocket] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`
147
- );
148
- this.reconnectTimer = setTimeout(() => {
149
- this.openConnection();
150
- }, delay);
151
- }
152
- }