@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,563 +0,0 @@
1
- /**
2
- * SupportChatModal — full-screen chat modal for human support conversations.
3
- * Shows message history (bubbles with timestamps/avatars), typing indicator, and reply input.
4
- * Supports native swipe-down-to-dismiss on iOS pageSheet.
5
- */
6
-
7
- import { useState, useEffect, useRef } from 'react';
8
- import {
9
- Modal,
10
- View,
11
- Text,
12
- TextInput,
13
- Pressable,
14
- ScrollView,
15
- StyleSheet,
16
- Platform,
17
- StatusBar,
18
- Keyboard,
19
- } from 'react-native';
20
- import type { AIMessage } from '../core/types';
21
- import { CloseIcon, SendArrowIcon, LoadingDots } from '../components/Icons';
22
-
23
- // ─── Props ─────────────────────────────────────────────────────
24
-
25
- interface SupportChatModalProps {
26
- visible: boolean;
27
- messages: AIMessage[];
28
- onSend: (message: string) => void;
29
- onClose: () => void;
30
- isAgentTyping?: boolean;
31
- isThinking?: boolean;
32
- /** Optional: externally controlled scroll trigger. Pass when messages update externally. */
33
- scrollToEndTrigger?: number;
34
- /** Ticket status — when 'closed' or 'resolved', input is hidden and a banner is shown. */
35
- ticketStatus?: string;
36
- }
37
-
38
- // ─── Helpers ───────────────────────────────────────────────────
39
-
40
- function formatRelativeTime(timestamp: number): string {
41
- const diff = Date.now() - timestamp;
42
- if (diff < 60000) return 'just now';
43
- if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
44
- if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
45
- return new Date(timestamp).toLocaleDateString();
46
- }
47
-
48
- function shouldShowDateSeparator(prev: AIMessage | undefined, curr: AIMessage): boolean {
49
- if (!prev) return true;
50
- const prevDay = new Date(prev.timestamp).toDateString();
51
- const currDay = new Date(curr.timestamp).toDateString();
52
- return prevDay !== currDay;
53
- }
54
-
55
- function formatDateSeparator(timestamp: number): string {
56
- const now = new Date();
57
- const date = new Date(timestamp);
58
- if (date.toDateString() === now.toDateString()) return 'Today';
59
- const yesterday = new Date(now);
60
- yesterday.setDate(yesterday.getDate() - 1);
61
- if (date.toDateString() === yesterday.toDateString()) return 'Yesterday';
62
- return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
63
- }
64
-
65
- // ─── Agent Avatar ──────────────────────────────────────────────
66
-
67
- function AgentAvatar() {
68
- return (
69
- <View style={s.agentAvatar}>
70
- <View style={s.avatarHead} />
71
- <View style={s.avatarBody} />
72
- </View>
73
- );
74
- }
75
-
76
- // ─── Main Component ────────────────────────────────────────────
77
-
78
- const CLOSED_STATUSES = ['closed', 'resolved'];
79
-
80
- export function SupportChatModal({
81
- visible,
82
- messages,
83
- onSend,
84
- onClose,
85
- isAgentTyping = false,
86
- isThinking = false,
87
- scrollToEndTrigger = 0,
88
- ticketStatus,
89
- }: SupportChatModalProps) {
90
- const isClosed = !!ticketStatus && CLOSED_STATUSES.includes(ticketStatus);
91
- const [text, setText] = useState('');
92
- const [keyboardHeight, setKeyboardHeight] = useState(0);
93
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
- const scrollRef = useRef<any>(null);
95
-
96
- // Scroll to bottom when new messages arrive or typing indicator changes
97
- useEffect(() => {
98
- if (messages.length > 0 || isAgentTyping) {
99
- setTimeout(() => scrollRef.current?.scrollToEnd?.({ animated: true }), 150);
100
- }
101
- }, [messages.length, isAgentTyping]);
102
-
103
- // Scroll when externally triggered (e.g., after message update in parent)
104
- useEffect(() => {
105
- if (scrollToEndTrigger && scrollToEndTrigger > 0) {
106
- setTimeout(() => scrollRef.current?.scrollToEnd?.({ animated: true }), 200);
107
- }
108
- }, [scrollToEndTrigger]);
109
-
110
- // Manually track keyboard height — reliable inside iOS pageSheet modals
111
- // where KeyboardAvoidingView miscalculates the offset from screen origin.
112
- useEffect(() => {
113
- const showSub = Keyboard.addListener('keyboardWillShow', (e) => {
114
- setKeyboardHeight(e.endCoordinates.height);
115
- setTimeout(() => scrollRef.current?.scrollToEnd?.({ animated: true }), 100);
116
- });
117
- const hideSub = Keyboard.addListener('keyboardWillHide', () => {
118
- setKeyboardHeight(0);
119
- });
120
- return () => {
121
- showSub.remove();
122
- hideSub.remove();
123
- };
124
- }, []);
125
-
126
- const handleSend = () => {
127
- if (!text.trim() || isThinking) return;
128
- onSend(text.trim());
129
- setText('');
130
- };
131
-
132
- const isEmpty = messages.length === 0 && !isAgentTyping;
133
-
134
- return (
135
- <Modal
136
- visible={visible}
137
- animationType="slide"
138
- presentationStyle="pageSheet"
139
- onRequestClose={onClose}
140
- >
141
- <StatusBar barStyle="light-content" />
142
- <View style={[s.container, keyboardHeight > 0 && { paddingBottom: keyboardHeight }]}>
143
- {/* Drag grip indicator */}
144
- <View style={s.dragHandle}>
145
- <View style={s.dragGrip} />
146
- </View>
147
-
148
- {/* ── Header ── */}
149
- <View style={s.header}>
150
- <Pressable onPress={onClose} style={s.headerBtn} hitSlop={12}>
151
- <CloseIcon size={20} color="rgba(255,255,255,0.7)" />
152
- </Pressable>
153
- <View style={s.headerCenter}>
154
- <Text style={s.headerTitle}>Support Chat</Text>
155
- <View style={s.headerStatus}>
156
- <View style={[s.statusDot, isClosed && s.statusDotClosed]} />
157
- <Text style={s.headerSubtitle}>
158
- {isClosed ? 'Conversation closed' : 'Agent online'}
159
- </Text>
160
- </View>
161
- </View>
162
- <View style={s.headerBtn} />
163
- </View>
164
-
165
- {/* ── Messages ── */}
166
- {isEmpty ? (
167
- <View style={s.emptyState}>
168
- <View style={s.emptyIcon}>
169
- <View style={s.emptyBubble} />
170
- <View style={s.emptyTail} />
171
- </View>
172
- <Text style={s.emptyTitle}>No messages yet</Text>
173
- <Text style={s.emptySubtitle}>Start the conversation below</Text>
174
- </View>
175
- ) : (
176
- <ScrollView
177
- ref={scrollRef}
178
- style={s.messagesList}
179
- contentContainerStyle={s.messagesContent}
180
- showsVerticalScrollIndicator={false}
181
- keyboardShouldPersistTaps="handled"
182
- >
183
- {messages.map((msg, i) => {
184
- const isUser = msg.role === 'user';
185
- const prev = messages[i - 1];
186
- const showDate = shouldShowDateSeparator(prev, msg);
187
-
188
- return (
189
- <View key={msg.id}>
190
- {/* Date separator */}
191
- {showDate && (
192
- <View style={s.dateSeparator}>
193
- <View style={s.dateLine} />
194
- <Text style={s.dateText}>{formatDateSeparator(msg.timestamp)}</Text>
195
- <View style={s.dateLine} />
196
- </View>
197
- )}
198
-
199
- {/* Message row */}
200
- <View style={[s.messageRow, isUser && s.messageRowUser]}>
201
- {/* Agent avatar (left side) */}
202
- {!isUser && <AgentAvatar />}
203
-
204
- <View style={s.bubbleColumn}>
205
- <View
206
- style={[
207
- s.bubble,
208
- isUser ? s.bubbleUser : s.bubbleAgent,
209
- ]}
210
- >
211
- <Text style={[s.bubbleText, !isUser && s.bubbleTextAgent]}>
212
- {msg.content}
213
- </Text>
214
- </View>
215
- <Text style={[s.timestamp, isUser && s.timestampUser]}>
216
- {formatRelativeTime(msg.timestamp)}
217
- </Text>
218
- </View>
219
- </View>
220
- </View>
221
- );
222
- })}
223
-
224
- {/* Typing indicator */}
225
- {isAgentTyping && (
226
- <View style={s.messageRow}>
227
- <AgentAvatar />
228
- <View style={s.bubbleColumn}>
229
- <View style={[s.bubble, s.bubbleAgent, s.typingBubble]}>
230
- <LoadingDots size={20} color="rgba(255,255,255,0.6)" />
231
- </View>
232
- </View>
233
- </View>
234
- )}
235
- </ScrollView>
236
- )}
237
-
238
- {/* ── Input Row or Closed Banner ── */}
239
- {isClosed ? (
240
- <View style={s.closedBanner}>
241
- <Text style={s.closedBannerText}>
242
- This conversation has been closed. Start a new request to get help.
243
- </Text>
244
- </View>
245
- ) : (
246
- <View style={s.inputRow}>
247
- <TextInput
248
- style={s.input}
249
- placeholder="Type a message..."
250
- placeholderTextColor="rgba(255,255,255,0.35)"
251
- value={text}
252
- onChangeText={setText}
253
- onSubmitEditing={handleSend}
254
- returnKeyType="send"
255
- editable={!isThinking}
256
- />
257
- <Pressable
258
- style={[s.sendBtn, text.trim() && !isThinking ? s.sendBtnActive : s.sendBtnInactive]}
259
- onPress={handleSend}
260
- disabled={!text.trim() || isThinking}
261
- >
262
- <SendArrowIcon size={18} color={text.trim() && !isThinking ? '#fff' : 'rgba(255,255,255,0.3)'} />
263
- </Pressable>
264
- </View>
265
- )}
266
- </View>
267
- </Modal>
268
- );
269
- }
270
-
271
- // ─── Styles ────────────────────────────────────────────────────
272
-
273
- const s = StyleSheet.create({
274
- container: {
275
- flex: 1,
276
- backgroundColor: '#0f0f1e',
277
- },
278
-
279
- // ── Drag Handle ──
280
- dragHandle: {
281
- alignItems: 'center',
282
- paddingTop: Platform.OS === 'ios' ? 52 : 16,
283
- paddingBottom: 6,
284
- },
285
- dragGrip: {
286
- width: 36,
287
- height: 5,
288
- borderRadius: 3,
289
- backgroundColor: 'rgba(255,255,255,0.2)',
290
- },
291
-
292
- // ── Header ──
293
- header: {
294
- flexDirection: 'row',
295
- alignItems: 'center',
296
- justifyContent: 'space-between',
297
- paddingHorizontal: 12,
298
- paddingTop: 10,
299
- paddingBottom: 14,
300
- backgroundColor: 'rgba(255,255,255,0.03)',
301
- borderBottomWidth: StyleSheet.hairlineWidth,
302
- borderBottomColor: 'rgba(255,255,255,0.08)',
303
- },
304
- headerBtn: {
305
- width: 36,
306
- height: 36,
307
- alignItems: 'center',
308
- justifyContent: 'center',
309
- },
310
- headerCenter: {
311
- alignItems: 'center',
312
- },
313
- headerTitle: {
314
- color: '#fff',
315
- fontSize: 17,
316
- fontWeight: '700',
317
- letterSpacing: 0.3,
318
- },
319
- headerStatus: {
320
- flexDirection: 'row',
321
- alignItems: 'center',
322
- gap: 5,
323
- marginTop: 3,
324
- },
325
- statusDot: {
326
- width: 7,
327
- height: 7,
328
- borderRadius: 4,
329
- backgroundColor: '#34C759',
330
- },
331
- statusDotClosed: {
332
- backgroundColor: '#8E8E93',
333
- },
334
- headerSubtitle: {
335
- color: 'rgba(255,255,255,0.5)',
336
- fontSize: 12,
337
- fontWeight: '500',
338
- },
339
-
340
- // ── Messages ──
341
- messagesList: {
342
- flex: 1,
343
- },
344
- messagesContent: {
345
- paddingHorizontal: 16,
346
- paddingVertical: 12,
347
- paddingBottom: 16,
348
- },
349
-
350
- // ── Date Separator ──
351
- dateSeparator: {
352
- flexDirection: 'row',
353
- alignItems: 'center',
354
- paddingVertical: 16,
355
- gap: 12,
356
- },
357
- dateLine: {
358
- flex: 1,
359
- height: StyleSheet.hairlineWidth,
360
- backgroundColor: 'rgba(255,255,255,0.08)',
361
- },
362
- dateText: {
363
- color: 'rgba(255,255,255,0.3)',
364
- fontSize: 11,
365
- fontWeight: '600',
366
- textTransform: 'uppercase',
367
- letterSpacing: 0.5,
368
- },
369
-
370
- // ── Message Row ──
371
- messageRow: {
372
- flexDirection: 'row',
373
- alignItems: 'flex-end',
374
- marginBottom: 4,
375
- gap: 8,
376
- },
377
- messageRowUser: {
378
- justifyContent: 'flex-end',
379
- },
380
- bubbleColumn: {
381
- maxWidth: '72%',
382
- },
383
-
384
- // ── Bubble ──
385
- bubble: {
386
- borderRadius: 18,
387
- paddingHorizontal: 14,
388
- paddingVertical: 10,
389
- },
390
- bubbleUser: {
391
- backgroundColor: '#7B68EE',
392
- borderBottomRightRadius: 6,
393
- elevation: 2,
394
- shadowColor: '#7B68EE',
395
- shadowOffset: { width: 0, height: 2 },
396
- shadowOpacity: 0.25,
397
- shadowRadius: 4,
398
- },
399
- bubbleAgent: {
400
- backgroundColor: 'rgba(255,255,255,0.08)',
401
- borderBottomLeftRadius: 6,
402
- },
403
- bubbleText: {
404
- fontSize: 15,
405
- lineHeight: 21,
406
- color: '#fff',
407
- },
408
- bubbleTextAgent: {
409
- color: 'rgba(255,255,255,0.9)',
410
- },
411
-
412
- // ── Timestamp ──
413
- timestamp: {
414
- color: 'rgba(255,255,255,0.25)',
415
- fontSize: 11,
416
- marginTop: 4,
417
- marginLeft: 4,
418
- marginBottom: 6,
419
- },
420
- timestampUser: {
421
- textAlign: 'right',
422
- marginRight: 4,
423
- marginLeft: 0,
424
- },
425
-
426
- // ── Agent Avatar ──
427
- agentAvatar: {
428
- width: 30,
429
- height: 30,
430
- borderRadius: 15,
431
- backgroundColor: '#7B68EE',
432
- alignItems: 'center',
433
- justifyContent: 'center',
434
- marginBottom: 14,
435
- },
436
- avatarHead: {
437
- width: 10,
438
- height: 10,
439
- borderRadius: 5,
440
- backgroundColor: 'rgba(255,255,255,0.9)',
441
- marginTop: 2,
442
- },
443
- avatarBody: {
444
- width: 16,
445
- height: 6,
446
- borderTopLeftRadius: 8,
447
- borderTopRightRadius: 8,
448
- backgroundColor: 'rgba(255,255,255,0.9)',
449
- marginTop: 1,
450
- },
451
-
452
- // ── Typing Indicator ──
453
- typingBubble: {
454
- flexDirection: 'row',
455
- alignItems: 'center',
456
- paddingVertical: 12,
457
- paddingHorizontal: 16,
458
- minWidth: 60,
459
- },
460
-
461
- // ── Empty State ──
462
- emptyState: {
463
- flex: 1,
464
- alignItems: 'center',
465
- justifyContent: 'center',
466
- paddingBottom: 60,
467
- },
468
- emptyIcon: {
469
- width: 64,
470
- height: 64,
471
- alignItems: 'center',
472
- justifyContent: 'center',
473
- marginBottom: 20,
474
- },
475
- emptyBubble: {
476
- width: 48,
477
- height: 36,
478
- borderRadius: 12,
479
- backgroundColor: 'rgba(255,255,255,0.08)',
480
- borderWidth: 1,
481
- borderColor: 'rgba(255,255,255,0.12)',
482
- },
483
- emptyTail: {
484
- position: 'absolute',
485
- bottom: 10,
486
- left: 16,
487
- width: 0,
488
- height: 0,
489
- borderTopWidth: 8,
490
- borderTopColor: 'rgba(255,255,255,0.08)',
491
- borderRightWidth: 8,
492
- borderRightColor: 'transparent',
493
- },
494
- emptyTitle: {
495
- color: 'rgba(255,255,255,0.5)',
496
- fontSize: 17,
497
- fontWeight: '600',
498
- marginBottom: 6,
499
- },
500
- emptySubtitle: {
501
- color: 'rgba(255,255,255,0.25)',
502
- fontSize: 14,
503
- },
504
-
505
- // ── Closed Banner ──
506
- closedBanner: {
507
- paddingHorizontal: 16,
508
- paddingVertical: 16,
509
- paddingBottom: Platform.OS === 'ios' ? 36 : 16,
510
- backgroundColor: 'rgba(255,255,255,0.03)',
511
- borderTopWidth: StyleSheet.hairlineWidth,
512
- borderTopColor: 'rgba(255,255,255,0.06)',
513
- alignItems: 'center',
514
- },
515
- closedBannerText: {
516
- color: 'rgba(255,255,255,0.35)',
517
- fontSize: 13,
518
- textAlign: 'center',
519
- lineHeight: 19,
520
- },
521
-
522
- // ── Input Row ──
523
- inputRow: {
524
- flexDirection: 'row',
525
- alignItems: 'center',
526
- gap: 10,
527
- paddingHorizontal: 16,
528
- paddingVertical: 12,
529
- paddingBottom: Platform.OS === 'ios' ? 36 : 16,
530
- backgroundColor: 'rgba(255,255,255,0.02)',
531
- borderTopWidth: StyleSheet.hairlineWidth,
532
- borderTopColor: 'rgba(255,255,255,0.06)',
533
- },
534
- input: {
535
- flex: 1,
536
- backgroundColor: 'rgba(255,255,255,0.06)',
537
- borderWidth: 1,
538
- borderColor: 'rgba(255,255,255,0.08)',
539
- borderRadius: 24,
540
- paddingHorizontal: 18,
541
- paddingVertical: 11,
542
- color: '#fff',
543
- fontSize: 16,
544
- },
545
- sendBtn: {
546
- width: 42,
547
- height: 42,
548
- borderRadius: 21,
549
- justifyContent: 'center',
550
- alignItems: 'center',
551
- },
552
- sendBtnActive: {
553
- backgroundColor: '#7B68EE',
554
- elevation: 3,
555
- shadowColor: '#7B68EE',
556
- shadowOffset: { width: 0, height: 2 },
557
- shadowOpacity: 0.3,
558
- shadowRadius: 4,
559
- },
560
- sendBtnInactive: {
561
- backgroundColor: 'rgba(255,255,255,0.06)',
562
- },
563
- });
@@ -1,161 +0,0 @@
1
- /**
2
- * Support Greeting & Quick Replies — shown when chat opens in support mode.
3
- *
4
- * Renders a welcome message with an avatar and quick reply buttons
5
- * that pre-fill the user's first message.
6
- */
7
-
8
-
9
- import {
10
- View,
11
- Text,
12
- TouchableOpacity,
13
- Image,
14
- StyleSheet,
15
- ScrollView,
16
- } from 'react-native';
17
- import type { SupportModeConfig } from './types';
18
-
19
- interface SupportGreetingProps {
20
- config: SupportModeConfig;
21
- onQuickReply: (message: string) => void;
22
- theme?: {
23
- primaryColor?: string;
24
- textColor?: string;
25
- backgroundColor?: string;
26
- };
27
- }
28
-
29
- export function SupportGreeting({
30
- config,
31
- onQuickReply,
32
- theme,
33
- }: SupportGreetingProps) {
34
- const greeting = config.greeting;
35
- const quickReplies = config.quickReplies ?? [];
36
-
37
- if (!greeting) return null;
38
-
39
- const primary = theme?.primaryColor ?? '#8b5cf6';
40
- const textColor = theme?.textColor ?? '#ffffff';
41
- const bgColor = theme?.backgroundColor ?? 'rgba(26, 26, 46, 0.95)';
42
-
43
- return (
44
- <View style={styles.container}>
45
- {/* Avatar + Agent Name */}
46
- <View style={styles.header}>
47
- {greeting.avatarUrl ? (
48
- <Image
49
- source={{ uri: greeting.avatarUrl }}
50
- style={styles.avatar}
51
- />
52
- ) : (
53
- <View style={[styles.avatarPlaceholder, { backgroundColor: primary }]}>
54
- <Text style={styles.avatarEmoji}>🤖</Text>
55
- </View>
56
- )}
57
- {greeting.agentName && (
58
- <Text style={[styles.agentName, { color: textColor }]}>
59
- {greeting.agentName}
60
- </Text>
61
- )}
62
- </View>
63
-
64
- {/* Greeting Message */}
65
- <View style={[styles.messageBubble, { backgroundColor: bgColor }]}>
66
- <Text style={[styles.messageText, { color: textColor }]}>
67
- {greeting.message}
68
- </Text>
69
- </View>
70
-
71
- {/* Quick Replies */}
72
- {quickReplies.length > 0 && (
73
- <ScrollView
74
- horizontal
75
- showsHorizontalScrollIndicator={false}
76
- contentContainerStyle={styles.quickRepliesContainer}
77
- >
78
- {quickReplies.map((reply, index) => (
79
- <TouchableOpacity
80
- key={`qr-${index}`}
81
- style={[styles.quickReplyButton, { borderColor: primary }]}
82
- onPress={() => onQuickReply(reply.message ?? reply.label)}
83
- activeOpacity={0.7}
84
- >
85
- {reply.icon && (
86
- <Text style={styles.quickReplyIcon}>{reply.icon}</Text>
87
- )}
88
- <Text style={[styles.quickReplyText, { color: primary }]}>
89
- {reply.label}
90
- </Text>
91
- </TouchableOpacity>
92
- ))}
93
- </ScrollView>
94
- )}
95
- </View>
96
- );
97
- }
98
-
99
- const styles = StyleSheet.create({
100
- container: {
101
- paddingHorizontal: 16,
102
- paddingVertical: 12,
103
- },
104
- header: {
105
- flexDirection: 'row',
106
- alignItems: 'center',
107
- gap: 10,
108
- marginBottom: 12,
109
- },
110
- avatar: {
111
- width: 36,
112
- height: 36,
113
- borderRadius: 18,
114
- },
115
- avatarPlaceholder: {
116
- width: 36,
117
- height: 36,
118
- borderRadius: 18,
119
- alignItems: 'center',
120
- justifyContent: 'center',
121
- },
122
- avatarEmoji: {
123
- fontSize: 18,
124
- },
125
- agentName: {
126
- fontSize: 14,
127
- fontWeight: '600',
128
- },
129
- messageBubble: {
130
- borderRadius: 16,
131
- borderTopLeftRadius: 4,
132
- paddingHorizontal: 14,
133
- paddingVertical: 10,
134
- marginBottom: 12,
135
- maxWidth: '85%',
136
- },
137
- messageText: {
138
- fontSize: 14,
139
- lineHeight: 20,
140
- },
141
- quickRepliesContainer: {
142
- gap: 8,
143
- paddingVertical: 4,
144
- },
145
- quickReplyButton: {
146
- flexDirection: 'row',
147
- alignItems: 'center',
148
- gap: 6,
149
- paddingHorizontal: 14,
150
- paddingVertical: 8,
151
- borderRadius: 20,
152
- borderWidth: 1,
153
- },
154
- quickReplyIcon: {
155
- fontSize: 14,
156
- },
157
- quickReplyText: {
158
- fontSize: 13,
159
- fontWeight: '500',
160
- },
161
- });