@repzo/widget-react-native 1.0.0

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 (448) hide show
  1. package/README.md +482 -0
  2. package/lib/commonjs/agent-ui-blocks.js +159 -0
  3. package/lib/commonjs/agent-ui-blocks.js.map +1 -0
  4. package/lib/commonjs/api/client.js +267 -0
  5. package/lib/commonjs/api/client.js.map +1 -0
  6. package/lib/commonjs/api/realtime.js +193 -0
  7. package/lib/commonjs/api/realtime.js.map +1 -0
  8. package/lib/commonjs/api/sse-parser.js +58 -0
  9. package/lib/commonjs/api/sse-parser.js.map +1 -0
  10. package/lib/commonjs/controller.js +23 -0
  11. package/lib/commonjs/controller.js.map +1 -0
  12. package/lib/commonjs/device-id.js +75 -0
  13. package/lib/commonjs/device-id.js.map +1 -0
  14. package/lib/commonjs/events.js +49 -0
  15. package/lib/commonjs/events.js.map +1 -0
  16. package/lib/commonjs/i18n.js +106 -0
  17. package/lib/commonjs/i18n.js.map +1 -0
  18. package/lib/commonjs/index.js +98 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/locales.js +85 -0
  21. package/lib/commonjs/locales.js.map +1 -0
  22. package/lib/commonjs/package.json +1 -0
  23. package/lib/commonjs/push/push-notifications.js +208 -0
  24. package/lib/commonjs/push/push-notifications.js.map +1 -0
  25. package/lib/commonjs/state/use-widget-state.js +23 -0
  26. package/lib/commonjs/state/use-widget-state.js.map +1 -0
  27. package/lib/commonjs/state/widget-store.js +1682 -0
  28. package/lib/commonjs/state/widget-store.js.map +1 -0
  29. package/lib/commonjs/storage.js +147 -0
  30. package/lib/commonjs/storage.js.map +1 -0
  31. package/lib/commonjs/types.js +44 -0
  32. package/lib/commonjs/types.js.map +1 -0
  33. package/lib/commonjs/ui/WidgetProvider.js +404 -0
  34. package/lib/commonjs/ui/WidgetProvider.js.map +1 -0
  35. package/lib/commonjs/ui/components/AgentUiBlocks.js +864 -0
  36. package/lib/commonjs/ui/components/AgentUiBlocks.js.map +1 -0
  37. package/lib/commonjs/ui/components/Avatar.js +76 -0
  38. package/lib/commonjs/ui/components/Avatar.js.map +1 -0
  39. package/lib/commonjs/ui/components/ConversationRow.js +155 -0
  40. package/lib/commonjs/ui/components/ConversationRow.js.map +1 -0
  41. package/lib/commonjs/ui/components/ErrorBoundary.js +44 -0
  42. package/lib/commonjs/ui/components/ErrorBoundary.js.map +1 -0
  43. package/lib/commonjs/ui/components/Gradient.js +168 -0
  44. package/lib/commonjs/ui/components/Gradient.js.map +1 -0
  45. package/lib/commonjs/ui/components/Launcher.js +125 -0
  46. package/lib/commonjs/ui/components/Launcher.js.map +1 -0
  47. package/lib/commonjs/ui/components/Markdown.js +257 -0
  48. package/lib/commonjs/ui/components/Markdown.js.map +1 -0
  49. package/lib/commonjs/ui/components/OutboundMessageCard.js +157 -0
  50. package/lib/commonjs/ui/components/OutboundMessageCard.js.map +1 -0
  51. package/lib/commonjs/ui/components/WidgetPanel.js +131 -0
  52. package/lib/commonjs/ui/components/WidgetPanel.js.map +1 -0
  53. package/lib/commonjs/ui/components/icons.js +383 -0
  54. package/lib/commonjs/ui/components/icons.js.map +1 -0
  55. package/lib/commonjs/ui/hooks/use-keyboard-overlap.js +113 -0
  56. package/lib/commonjs/ui/hooks/use-keyboard-overlap.js.map +1 -0
  57. package/lib/commonjs/ui/hooks/use-reduce-motion.js +38 -0
  58. package/lib/commonjs/ui/hooks/use-reduce-motion.js.map +1 -0
  59. package/lib/commonjs/ui/hooks/use-widget-insets.js +41 -0
  60. package/lib/commonjs/ui/hooks/use-widget-insets.js.map +1 -0
  61. package/lib/commonjs/ui/screens/chat/ChatView.js +590 -0
  62. package/lib/commonjs/ui/screens/chat/ChatView.js.map +1 -0
  63. package/lib/commonjs/ui/screens/chat/ChatView.styles.js +362 -0
  64. package/lib/commonjs/ui/screens/chat/ChatView.styles.js.map +1 -0
  65. package/lib/commonjs/ui/screens/chat/ChatView.types.js +6 -0
  66. package/lib/commonjs/ui/screens/chat/ChatView.types.js.map +1 -0
  67. package/lib/commonjs/ui/screens/conversations/ConversationsList.js +150 -0
  68. package/lib/commonjs/ui/screens/conversations/ConversationsList.js.map +1 -0
  69. package/lib/commonjs/ui/screens/conversations/ConversationsList.styles.js +118 -0
  70. package/lib/commonjs/ui/screens/conversations/ConversationsList.styles.js.map +1 -0
  71. package/lib/commonjs/ui/screens/conversations/ConversationsList.types.js +6 -0
  72. package/lib/commonjs/ui/screens/conversations/ConversationsList.types.js.map +1 -0
  73. package/lib/commonjs/ui/screens/home/HomeView.js +336 -0
  74. package/lib/commonjs/ui/screens/home/HomeView.js.map +1 -0
  75. package/lib/commonjs/ui/screens/home/HomeView.styles.js +245 -0
  76. package/lib/commonjs/ui/screens/home/HomeView.styles.js.map +1 -0
  77. package/lib/commonjs/ui/screens/home/HomeView.types.js +6 -0
  78. package/lib/commonjs/ui/screens/home/HomeView.types.js.map +1 -0
  79. package/lib/commonjs/ui/screens/home/greeting.js +49 -0
  80. package/lib/commonjs/ui/screens/home/greeting.js.map +1 -0
  81. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.js +229 -0
  82. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.js.map +1 -0
  83. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.styles.js +125 -0
  84. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.styles.js.map +1 -0
  85. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.types.js +6 -0
  86. package/lib/commonjs/ui/screens/pre-chat/PreChatForm.types.js.map +1 -0
  87. package/lib/commonjs/ui/styles/global.js +55 -0
  88. package/lib/commonjs/ui/styles/global.js.map +1 -0
  89. package/lib/commonjs/ui/styles/theme.js +148 -0
  90. package/lib/commonjs/ui/styles/theme.js.map +1 -0
  91. package/lib/commonjs/ui/utils/conversations.js +125 -0
  92. package/lib/commonjs/ui/utils/conversations.js.map +1 -0
  93. package/lib/commonjs/ui/utils/html.js +44 -0
  94. package/lib/commonjs/ui/utils/html.js.map +1 -0
  95. package/lib/commonjs/ui/utils/native-gradient.js +65 -0
  96. package/lib/commonjs/ui/utils/native-gradient.js.map +1 -0
  97. package/lib/commonjs/ui/utils/rtl.js +56 -0
  98. package/lib/commonjs/ui/utils/rtl.js.map +1 -0
  99. package/lib/commonjs/ui/utils/safe-url.js +67 -0
  100. package/lib/commonjs/ui/utils/safe-url.js.map +1 -0
  101. package/lib/module/agent-ui-blocks.js +152 -0
  102. package/lib/module/agent-ui-blocks.js.map +1 -0
  103. package/lib/module/api/client.js +261 -0
  104. package/lib/module/api/client.js.map +1 -0
  105. package/lib/module/api/realtime.js +189 -0
  106. package/lib/module/api/realtime.js.map +1 -0
  107. package/lib/module/api/sse-parser.js +53 -0
  108. package/lib/module/api/sse-parser.js.map +1 -0
  109. package/lib/module/controller.js +19 -0
  110. package/lib/module/controller.js.map +1 -0
  111. package/lib/module/device-id.js +69 -0
  112. package/lib/module/device-id.js.map +1 -0
  113. package/lib/module/events.js +44 -0
  114. package/lib/module/events.js.map +1 -0
  115. package/lib/module/i18n.js +99 -0
  116. package/lib/module/i18n.js.map +1 -0
  117. package/lib/module/index.js +55 -0
  118. package/lib/module/index.js.map +1 -0
  119. package/lib/module/locales.js +80 -0
  120. package/lib/module/locales.js.map +1 -0
  121. package/lib/module/package.json +1 -0
  122. package/lib/module/push/push-notifications.js +204 -0
  123. package/lib/module/push/push-notifications.js.map +1 -0
  124. package/lib/module/state/use-widget-state.js +18 -0
  125. package/lib/module/state/use-widget-state.js.map +1 -0
  126. package/lib/module/state/widget-store.js +1677 -0
  127. package/lib/module/state/widget-store.js.map +1 -0
  128. package/lib/module/storage.js +141 -0
  129. package/lib/module/storage.js.map +1 -0
  130. package/lib/module/types.js +40 -0
  131. package/lib/module/types.js.map +1 -0
  132. package/lib/module/ui/WidgetProvider.js +400 -0
  133. package/lib/module/ui/WidgetProvider.js.map +1 -0
  134. package/lib/module/ui/components/AgentUiBlocks.js +857 -0
  135. package/lib/module/ui/components/AgentUiBlocks.js.map +1 -0
  136. package/lib/module/ui/components/Avatar.js +71 -0
  137. package/lib/module/ui/components/Avatar.js.map +1 -0
  138. package/lib/module/ui/components/ConversationRow.js +150 -0
  139. package/lib/module/ui/components/ConversationRow.js.map +1 -0
  140. package/lib/module/ui/components/ErrorBoundary.js +39 -0
  141. package/lib/module/ui/components/ErrorBoundary.js.map +1 -0
  142. package/lib/module/ui/components/Gradient.js +165 -0
  143. package/lib/module/ui/components/Gradient.js.map +1 -0
  144. package/lib/module/ui/components/Launcher.js +121 -0
  145. package/lib/module/ui/components/Launcher.js.map +1 -0
  146. package/lib/module/ui/components/Markdown.js +253 -0
  147. package/lib/module/ui/components/Markdown.js.map +1 -0
  148. package/lib/module/ui/components/OutboundMessageCard.js +153 -0
  149. package/lib/module/ui/components/OutboundMessageCard.js.map +1 -0
  150. package/lib/module/ui/components/WidgetPanel.js +127 -0
  151. package/lib/module/ui/components/WidgetPanel.js.map +1 -0
  152. package/lib/module/ui/components/icons.js +370 -0
  153. package/lib/module/ui/components/icons.js.map +1 -0
  154. package/lib/module/ui/hooks/use-keyboard-overlap.js +109 -0
  155. package/lib/module/ui/hooks/use-keyboard-overlap.js.map +1 -0
  156. package/lib/module/ui/hooks/use-reduce-motion.js +34 -0
  157. package/lib/module/ui/hooks/use-reduce-motion.js.map +1 -0
  158. package/lib/module/ui/hooks/use-widget-insets.js +37 -0
  159. package/lib/module/ui/hooks/use-widget-insets.js.map +1 -0
  160. package/lib/module/ui/screens/chat/ChatView.js +586 -0
  161. package/lib/module/ui/screens/chat/ChatView.js.map +1 -0
  162. package/lib/module/ui/screens/chat/ChatView.styles.js +358 -0
  163. package/lib/module/ui/screens/chat/ChatView.styles.js.map +1 -0
  164. package/lib/module/ui/screens/chat/ChatView.types.js +4 -0
  165. package/lib/module/ui/screens/chat/ChatView.types.js.map +1 -0
  166. package/lib/module/ui/screens/conversations/ConversationsList.js +146 -0
  167. package/lib/module/ui/screens/conversations/ConversationsList.js.map +1 -0
  168. package/lib/module/ui/screens/conversations/ConversationsList.styles.js +114 -0
  169. package/lib/module/ui/screens/conversations/ConversationsList.styles.js.map +1 -0
  170. package/lib/module/ui/screens/conversations/ConversationsList.types.js +4 -0
  171. package/lib/module/ui/screens/conversations/ConversationsList.types.js.map +1 -0
  172. package/lib/module/ui/screens/home/HomeView.js +332 -0
  173. package/lib/module/ui/screens/home/HomeView.js.map +1 -0
  174. package/lib/module/ui/screens/home/HomeView.styles.js +242 -0
  175. package/lib/module/ui/screens/home/HomeView.styles.js.map +1 -0
  176. package/lib/module/ui/screens/home/HomeView.types.js +4 -0
  177. package/lib/module/ui/screens/home/HomeView.types.js.map +1 -0
  178. package/lib/module/ui/screens/home/greeting.js +44 -0
  179. package/lib/module/ui/screens/home/greeting.js.map +1 -0
  180. package/lib/module/ui/screens/pre-chat/PreChatForm.js +225 -0
  181. package/lib/module/ui/screens/pre-chat/PreChatForm.js.map +1 -0
  182. package/lib/module/ui/screens/pre-chat/PreChatForm.styles.js +121 -0
  183. package/lib/module/ui/screens/pre-chat/PreChatForm.styles.js.map +1 -0
  184. package/lib/module/ui/screens/pre-chat/PreChatForm.types.js +4 -0
  185. package/lib/module/ui/screens/pre-chat/PreChatForm.types.js.map +1 -0
  186. package/lib/module/ui/styles/global.js +51 -0
  187. package/lib/module/ui/styles/global.js.map +1 -0
  188. package/lib/module/ui/styles/theme.js +137 -0
  189. package/lib/module/ui/styles/theme.js.map +1 -0
  190. package/lib/module/ui/utils/conversations.js +115 -0
  191. package/lib/module/ui/utils/conversations.js.map +1 -0
  192. package/lib/module/ui/utils/html.js +39 -0
  193. package/lib/module/ui/utils/html.js.map +1 -0
  194. package/lib/module/ui/utils/native-gradient.js +60 -0
  195. package/lib/module/ui/utils/native-gradient.js.map +1 -0
  196. package/lib/module/ui/utils/rtl.js +49 -0
  197. package/lib/module/ui/utils/rtl.js.map +1 -0
  198. package/lib/module/ui/utils/safe-url.js +59 -0
  199. package/lib/module/ui/utils/safe-url.js.map +1 -0
  200. package/lib/typescript/commonjs/agent-ui-blocks.d.ts +20 -0
  201. package/lib/typescript/commonjs/agent-ui-blocks.d.ts.map +1 -0
  202. package/lib/typescript/commonjs/api/client.d.ts +115 -0
  203. package/lib/typescript/commonjs/api/client.d.ts.map +1 -0
  204. package/lib/typescript/commonjs/api/realtime.d.ts +39 -0
  205. package/lib/typescript/commonjs/api/realtime.d.ts.map +1 -0
  206. package/lib/typescript/commonjs/api/sse-parser.d.ts +13 -0
  207. package/lib/typescript/commonjs/api/sse-parser.d.ts.map +1 -0
  208. package/lib/typescript/commonjs/controller.d.ts +15 -0
  209. package/lib/typescript/commonjs/controller.d.ts.map +1 -0
  210. package/lib/typescript/commonjs/device-id.d.ts +15 -0
  211. package/lib/typescript/commonjs/device-id.d.ts.map +1 -0
  212. package/lib/typescript/commonjs/events.d.ts +41 -0
  213. package/lib/typescript/commonjs/events.d.ts.map +1 -0
  214. package/lib/typescript/commonjs/i18n.d.ts +78 -0
  215. package/lib/typescript/commonjs/i18n.d.ts.map +1 -0
  216. package/lib/typescript/commonjs/index.d.ts +103 -0
  217. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  218. package/lib/typescript/commonjs/locales.d.ts +18 -0
  219. package/lib/typescript/commonjs/locales.d.ts.map +1 -0
  220. package/lib/typescript/commonjs/package.json +1 -0
  221. package/lib/typescript/commonjs/push/push-notifications.d.ts +71 -0
  222. package/lib/typescript/commonjs/push/push-notifications.d.ts.map +1 -0
  223. package/lib/typescript/commonjs/state/use-widget-state.d.ts +10 -0
  224. package/lib/typescript/commonjs/state/use-widget-state.d.ts.map +1 -0
  225. package/lib/typescript/commonjs/state/widget-store.d.ts +227 -0
  226. package/lib/typescript/commonjs/state/widget-store.d.ts.map +1 -0
  227. package/lib/typescript/commonjs/storage.d.ts +42 -0
  228. package/lib/typescript/commonjs/storage.d.ts.map +1 -0
  229. package/lib/typescript/commonjs/types.d.ts +391 -0
  230. package/lib/typescript/commonjs/types.d.ts.map +1 -0
  231. package/lib/typescript/commonjs/ui/WidgetProvider.d.ts +15 -0
  232. package/lib/typescript/commonjs/ui/WidgetProvider.d.ts.map +1 -0
  233. package/lib/typescript/commonjs/ui/components/AgentUiBlocks.d.ts +27 -0
  234. package/lib/typescript/commonjs/ui/components/AgentUiBlocks.d.ts.map +1 -0
  235. package/lib/typescript/commonjs/ui/components/Avatar.d.ts +19 -0
  236. package/lib/typescript/commonjs/ui/components/Avatar.d.ts.map +1 -0
  237. package/lib/typescript/commonjs/ui/components/ConversationRow.d.ts +26 -0
  238. package/lib/typescript/commonjs/ui/components/ConversationRow.d.ts.map +1 -0
  239. package/lib/typescript/commonjs/ui/components/ErrorBoundary.d.ts +27 -0
  240. package/lib/typescript/commonjs/ui/components/ErrorBoundary.d.ts.map +1 -0
  241. package/lib/typescript/commonjs/ui/components/Gradient.d.ts +33 -0
  242. package/lib/typescript/commonjs/ui/components/Gradient.d.ts.map +1 -0
  243. package/lib/typescript/commonjs/ui/components/Launcher.d.ts +18 -0
  244. package/lib/typescript/commonjs/ui/components/Launcher.d.ts.map +1 -0
  245. package/lib/typescript/commonjs/ui/components/Markdown.d.ts +19 -0
  246. package/lib/typescript/commonjs/ui/components/Markdown.d.ts.map +1 -0
  247. package/lib/typescript/commonjs/ui/components/OutboundMessageCard.d.ts +20 -0
  248. package/lib/typescript/commonjs/ui/components/OutboundMessageCard.d.ts.map +1 -0
  249. package/lib/typescript/commonjs/ui/components/WidgetPanel.d.ts +26 -0
  250. package/lib/typescript/commonjs/ui/components/WidgetPanel.d.ts.map +1 -0
  251. package/lib/typescript/commonjs/ui/components/icons.d.ts +38 -0
  252. package/lib/typescript/commonjs/ui/components/icons.d.ts.map +1 -0
  253. package/lib/typescript/commonjs/ui/hooks/use-keyboard-overlap.d.ts +33 -0
  254. package/lib/typescript/commonjs/ui/hooks/use-keyboard-overlap.d.ts.map +1 -0
  255. package/lib/typescript/commonjs/ui/hooks/use-reduce-motion.d.ts +11 -0
  256. package/lib/typescript/commonjs/ui/hooks/use-reduce-motion.d.ts.map +1 -0
  257. package/lib/typescript/commonjs/ui/hooks/use-widget-insets.d.ts +16 -0
  258. package/lib/typescript/commonjs/ui/hooks/use-widget-insets.d.ts.map +1 -0
  259. package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts +11 -0
  260. package/lib/typescript/commonjs/ui/screens/chat/ChatView.d.ts.map +1 -0
  261. package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts +349 -0
  262. package/lib/typescript/commonjs/ui/screens/chat/ChatView.styles.d.ts.map +1 -0
  263. package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts +48 -0
  264. package/lib/typescript/commonjs/ui/screens/chat/ChatView.types.d.ts.map +1 -0
  265. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.d.ts +10 -0
  266. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.d.ts.map +1 -0
  267. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.styles.d.ts +113 -0
  268. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.styles.d.ts.map +1 -0
  269. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.types.d.ts +31 -0
  270. package/lib/typescript/commonjs/ui/screens/conversations/ConversationsList.types.d.ts.map +1 -0
  271. package/lib/typescript/commonjs/ui/screens/home/HomeView.d.ts +17 -0
  272. package/lib/typescript/commonjs/ui/screens/home/HomeView.d.ts.map +1 -0
  273. package/lib/typescript/commonjs/ui/screens/home/HomeView.styles.d.ts +227 -0
  274. package/lib/typescript/commonjs/ui/screens/home/HomeView.styles.d.ts.map +1 -0
  275. package/lib/typescript/commonjs/ui/screens/home/HomeView.types.d.ts +40 -0
  276. package/lib/typescript/commonjs/ui/screens/home/HomeView.types.d.ts.map +1 -0
  277. package/lib/typescript/commonjs/ui/screens/home/greeting.d.ts +19 -0
  278. package/lib/typescript/commonjs/ui/screens/home/greeting.d.ts.map +1 -0
  279. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.d.ts +7 -0
  280. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.d.ts.map +1 -0
  281. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.styles.d.ts +115 -0
  282. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.styles.d.ts.map +1 -0
  283. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.types.d.ts +20 -0
  284. package/lib/typescript/commonjs/ui/screens/pre-chat/PreChatForm.types.d.ts.map +1 -0
  285. package/lib/typescript/commonjs/ui/styles/global.d.ts +46 -0
  286. package/lib/typescript/commonjs/ui/styles/global.d.ts.map +1 -0
  287. package/lib/typescript/commonjs/ui/styles/theme.d.ts +103 -0
  288. package/lib/typescript/commonjs/ui/styles/theme.d.ts.map +1 -0
  289. package/lib/typescript/commonjs/ui/utils/conversations.d.ts +41 -0
  290. package/lib/typescript/commonjs/ui/utils/conversations.d.ts.map +1 -0
  291. package/lib/typescript/commonjs/ui/utils/html.d.ts +10 -0
  292. package/lib/typescript/commonjs/ui/utils/html.d.ts.map +1 -0
  293. package/lib/typescript/commonjs/ui/utils/native-gradient.d.ts +61 -0
  294. package/lib/typescript/commonjs/ui/utils/native-gradient.d.ts.map +1 -0
  295. package/lib/typescript/commonjs/ui/utils/rtl.d.ts +33 -0
  296. package/lib/typescript/commonjs/ui/utils/rtl.d.ts.map +1 -0
  297. package/lib/typescript/commonjs/ui/utils/safe-url.d.ts +21 -0
  298. package/lib/typescript/commonjs/ui/utils/safe-url.d.ts.map +1 -0
  299. package/lib/typescript/module/agent-ui-blocks.d.ts +20 -0
  300. package/lib/typescript/module/agent-ui-blocks.d.ts.map +1 -0
  301. package/lib/typescript/module/api/client.d.ts +115 -0
  302. package/lib/typescript/module/api/client.d.ts.map +1 -0
  303. package/lib/typescript/module/api/realtime.d.ts +39 -0
  304. package/lib/typescript/module/api/realtime.d.ts.map +1 -0
  305. package/lib/typescript/module/api/sse-parser.d.ts +13 -0
  306. package/lib/typescript/module/api/sse-parser.d.ts.map +1 -0
  307. package/lib/typescript/module/controller.d.ts +15 -0
  308. package/lib/typescript/module/controller.d.ts.map +1 -0
  309. package/lib/typescript/module/device-id.d.ts +15 -0
  310. package/lib/typescript/module/device-id.d.ts.map +1 -0
  311. package/lib/typescript/module/events.d.ts +41 -0
  312. package/lib/typescript/module/events.d.ts.map +1 -0
  313. package/lib/typescript/module/i18n.d.ts +78 -0
  314. package/lib/typescript/module/i18n.d.ts.map +1 -0
  315. package/lib/typescript/module/index.d.ts +103 -0
  316. package/lib/typescript/module/index.d.ts.map +1 -0
  317. package/lib/typescript/module/locales.d.ts +18 -0
  318. package/lib/typescript/module/locales.d.ts.map +1 -0
  319. package/lib/typescript/module/package.json +1 -0
  320. package/lib/typescript/module/push/push-notifications.d.ts +71 -0
  321. package/lib/typescript/module/push/push-notifications.d.ts.map +1 -0
  322. package/lib/typescript/module/state/use-widget-state.d.ts +10 -0
  323. package/lib/typescript/module/state/use-widget-state.d.ts.map +1 -0
  324. package/lib/typescript/module/state/widget-store.d.ts +227 -0
  325. package/lib/typescript/module/state/widget-store.d.ts.map +1 -0
  326. package/lib/typescript/module/storage.d.ts +42 -0
  327. package/lib/typescript/module/storage.d.ts.map +1 -0
  328. package/lib/typescript/module/types.d.ts +391 -0
  329. package/lib/typescript/module/types.d.ts.map +1 -0
  330. package/lib/typescript/module/ui/WidgetProvider.d.ts +15 -0
  331. package/lib/typescript/module/ui/WidgetProvider.d.ts.map +1 -0
  332. package/lib/typescript/module/ui/components/AgentUiBlocks.d.ts +27 -0
  333. package/lib/typescript/module/ui/components/AgentUiBlocks.d.ts.map +1 -0
  334. package/lib/typescript/module/ui/components/Avatar.d.ts +19 -0
  335. package/lib/typescript/module/ui/components/Avatar.d.ts.map +1 -0
  336. package/lib/typescript/module/ui/components/ConversationRow.d.ts +26 -0
  337. package/lib/typescript/module/ui/components/ConversationRow.d.ts.map +1 -0
  338. package/lib/typescript/module/ui/components/ErrorBoundary.d.ts +27 -0
  339. package/lib/typescript/module/ui/components/ErrorBoundary.d.ts.map +1 -0
  340. package/lib/typescript/module/ui/components/Gradient.d.ts +33 -0
  341. package/lib/typescript/module/ui/components/Gradient.d.ts.map +1 -0
  342. package/lib/typescript/module/ui/components/Launcher.d.ts +18 -0
  343. package/lib/typescript/module/ui/components/Launcher.d.ts.map +1 -0
  344. package/lib/typescript/module/ui/components/Markdown.d.ts +19 -0
  345. package/lib/typescript/module/ui/components/Markdown.d.ts.map +1 -0
  346. package/lib/typescript/module/ui/components/OutboundMessageCard.d.ts +20 -0
  347. package/lib/typescript/module/ui/components/OutboundMessageCard.d.ts.map +1 -0
  348. package/lib/typescript/module/ui/components/WidgetPanel.d.ts +26 -0
  349. package/lib/typescript/module/ui/components/WidgetPanel.d.ts.map +1 -0
  350. package/lib/typescript/module/ui/components/icons.d.ts +38 -0
  351. package/lib/typescript/module/ui/components/icons.d.ts.map +1 -0
  352. package/lib/typescript/module/ui/hooks/use-keyboard-overlap.d.ts +33 -0
  353. package/lib/typescript/module/ui/hooks/use-keyboard-overlap.d.ts.map +1 -0
  354. package/lib/typescript/module/ui/hooks/use-reduce-motion.d.ts +11 -0
  355. package/lib/typescript/module/ui/hooks/use-reduce-motion.d.ts.map +1 -0
  356. package/lib/typescript/module/ui/hooks/use-widget-insets.d.ts +16 -0
  357. package/lib/typescript/module/ui/hooks/use-widget-insets.d.ts.map +1 -0
  358. package/lib/typescript/module/ui/screens/chat/ChatView.d.ts +11 -0
  359. package/lib/typescript/module/ui/screens/chat/ChatView.d.ts.map +1 -0
  360. package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts +349 -0
  361. package/lib/typescript/module/ui/screens/chat/ChatView.styles.d.ts.map +1 -0
  362. package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts +48 -0
  363. package/lib/typescript/module/ui/screens/chat/ChatView.types.d.ts.map +1 -0
  364. package/lib/typescript/module/ui/screens/conversations/ConversationsList.d.ts +10 -0
  365. package/lib/typescript/module/ui/screens/conversations/ConversationsList.d.ts.map +1 -0
  366. package/lib/typescript/module/ui/screens/conversations/ConversationsList.styles.d.ts +113 -0
  367. package/lib/typescript/module/ui/screens/conversations/ConversationsList.styles.d.ts.map +1 -0
  368. package/lib/typescript/module/ui/screens/conversations/ConversationsList.types.d.ts +31 -0
  369. package/lib/typescript/module/ui/screens/conversations/ConversationsList.types.d.ts.map +1 -0
  370. package/lib/typescript/module/ui/screens/home/HomeView.d.ts +17 -0
  371. package/lib/typescript/module/ui/screens/home/HomeView.d.ts.map +1 -0
  372. package/lib/typescript/module/ui/screens/home/HomeView.styles.d.ts +227 -0
  373. package/lib/typescript/module/ui/screens/home/HomeView.styles.d.ts.map +1 -0
  374. package/lib/typescript/module/ui/screens/home/HomeView.types.d.ts +40 -0
  375. package/lib/typescript/module/ui/screens/home/HomeView.types.d.ts.map +1 -0
  376. package/lib/typescript/module/ui/screens/home/greeting.d.ts +19 -0
  377. package/lib/typescript/module/ui/screens/home/greeting.d.ts.map +1 -0
  378. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.d.ts +7 -0
  379. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.d.ts.map +1 -0
  380. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.styles.d.ts +115 -0
  381. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.styles.d.ts.map +1 -0
  382. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.types.d.ts +20 -0
  383. package/lib/typescript/module/ui/screens/pre-chat/PreChatForm.types.d.ts.map +1 -0
  384. package/lib/typescript/module/ui/styles/global.d.ts +46 -0
  385. package/lib/typescript/module/ui/styles/global.d.ts.map +1 -0
  386. package/lib/typescript/module/ui/styles/theme.d.ts +103 -0
  387. package/lib/typescript/module/ui/styles/theme.d.ts.map +1 -0
  388. package/lib/typescript/module/ui/utils/conversations.d.ts +41 -0
  389. package/lib/typescript/module/ui/utils/conversations.d.ts.map +1 -0
  390. package/lib/typescript/module/ui/utils/html.d.ts +10 -0
  391. package/lib/typescript/module/ui/utils/html.d.ts.map +1 -0
  392. package/lib/typescript/module/ui/utils/native-gradient.d.ts +61 -0
  393. package/lib/typescript/module/ui/utils/native-gradient.d.ts.map +1 -0
  394. package/lib/typescript/module/ui/utils/rtl.d.ts +33 -0
  395. package/lib/typescript/module/ui/utils/rtl.d.ts.map +1 -0
  396. package/lib/typescript/module/ui/utils/safe-url.d.ts +21 -0
  397. package/lib/typescript/module/ui/utils/safe-url.d.ts.map +1 -0
  398. package/metro.js +40 -0
  399. package/package.json +116 -0
  400. package/src/agent-ui-blocks.ts +302 -0
  401. package/src/api/client.ts +332 -0
  402. package/src/api/realtime.ts +225 -0
  403. package/src/api/sse-parser.ts +51 -0
  404. package/src/controller.ts +20 -0
  405. package/src/device-id.ts +71 -0
  406. package/src/events.ts +74 -0
  407. package/src/i18n.ts +114 -0
  408. package/src/index.tsx +199 -0
  409. package/src/locales.ts +80 -0
  410. package/src/push/push-notifications.ts +254 -0
  411. package/src/state/use-widget-state.ts +20 -0
  412. package/src/state/widget-store.ts +1815 -0
  413. package/src/storage.ts +153 -0
  414. package/src/types.ts +445 -0
  415. package/src/ui/WidgetProvider.tsx +434 -0
  416. package/src/ui/components/AgentUiBlocks.tsx +912 -0
  417. package/src/ui/components/Avatar.tsx +86 -0
  418. package/src/ui/components/ConversationRow.tsx +163 -0
  419. package/src/ui/components/ErrorBoundary.tsx +49 -0
  420. package/src/ui/components/Gradient.tsx +165 -0
  421. package/src/ui/components/Launcher.tsx +128 -0
  422. package/src/ui/components/Markdown.tsx +291 -0
  423. package/src/ui/components/OutboundMessageCard.tsx +154 -0
  424. package/src/ui/components/WidgetPanel.tsx +133 -0
  425. package/src/ui/components/icons.tsx +315 -0
  426. package/src/ui/hooks/use-keyboard-overlap.ts +134 -0
  427. package/src/ui/hooks/use-reduce-motion.ts +39 -0
  428. package/src/ui/hooks/use-widget-insets.ts +44 -0
  429. package/src/ui/screens/chat/ChatView.styles.ts +357 -0
  430. package/src/ui/screens/chat/ChatView.tsx +690 -0
  431. package/src/ui/screens/chat/ChatView.types.ts +46 -0
  432. package/src/ui/screens/conversations/ConversationsList.styles.ts +111 -0
  433. package/src/ui/screens/conversations/ConversationsList.tsx +144 -0
  434. package/src/ui/screens/conversations/ConversationsList.types.ts +33 -0
  435. package/src/ui/screens/home/HomeView.styles.ts +246 -0
  436. package/src/ui/screens/home/HomeView.tsx +336 -0
  437. package/src/ui/screens/home/HomeView.types.ts +42 -0
  438. package/src/ui/screens/home/greeting.ts +50 -0
  439. package/src/ui/screens/pre-chat/PreChatForm.styles.ts +120 -0
  440. package/src/ui/screens/pre-chat/PreChatForm.tsx +221 -0
  441. package/src/ui/screens/pre-chat/PreChatForm.types.ts +22 -0
  442. package/src/ui/styles/global.ts +50 -0
  443. package/src/ui/styles/theme.ts +155 -0
  444. package/src/ui/utils/conversations.ts +122 -0
  445. package/src/ui/utils/html.ts +58 -0
  446. package/src/ui/utils/native-gradient.ts +60 -0
  447. package/src/ui/utils/rtl.ts +43 -0
  448. package/src/ui/utils/safe-url.ts +63 -0
@@ -0,0 +1,690 @@
1
+ /**
2
+ * Chat thread — message list, typing indicator and composer.
3
+ * Port of `client/src/widget/components/ChatView.tsx`.
4
+ *
5
+ * Mobile differences: older messages load through an explicit "Load earlier
6
+ * messages" control (a scroll-anchored infinite list fights the keyboard), and
7
+ * the composer hides the attach button because v1 has no picker.
8
+ */
9
+
10
+ import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
11
+ import {
12
+ Animated,
13
+ Easing,
14
+ Image,
15
+ Pressable,
16
+ ScrollView,
17
+ Text,
18
+ TextInput,
19
+ View,
20
+ } from 'react-native';
21
+ import type { LayoutChangeEvent } from 'react-native';
22
+
23
+ import {
24
+ formatAgentUiActionMessage,
25
+ getAgentUiBlocks,
26
+ getProcedureForm,
27
+ } from '../../../agent-ui-blocks';
28
+ import type { AgentUiAction, AgentProcedureForm, Message, WidgetAttachment } from '../../../types';
29
+ import { AgentUiBlocks } from '../../components/AgentUiBlocks';
30
+ import { Avatar } from '../../components/Avatar';
31
+ import { WidgetErrorBoundary } from '../../components/ErrorBoundary';
32
+ import { GradientBackground } from '../../components/Gradient';
33
+ import { ArrowUpIcon, ChatOutlineIcon, ChevronIcon } from '../../components/icons';
34
+ import { Markdown } from '../../components/Markdown';
35
+ import { useReduceMotion } from '../../hooks/use-reduce-motion';
36
+ import { globalStyles } from '../../styles/global';
37
+ import { contrastingTextColor, mixHex, palette, withAlpha } from '../../styles/theme';
38
+ import { formatClockTime, formatDayLabel, isSameDay } from '../../utils/conversations';
39
+ import { stripHtml } from '../../utils/html';
40
+ import { openExternalUrl } from '../../utils/safe-url';
41
+ import { backDirection, isRTL } from '../../utils/rtl';
42
+ import { styles } from './ChatView.styles';
43
+ import type { ChatLabels, ChatViewProps } from './ChatView.types';
44
+
45
+ export function ChatView({
46
+ messages,
47
+ loading,
48
+ hasMoreMessages,
49
+ agentTyping,
50
+ color,
51
+ agentName,
52
+ agentAvatar,
53
+ isOnline,
54
+ locale,
55
+ prefillText,
56
+ offlineMessage,
57
+ labels,
58
+ onSendMessage,
59
+ onIdentifyVisitor,
60
+ onLoadMore,
61
+ onTyping,
62
+ onBack,
63
+ onClose,
64
+ }: ChatViewProps) {
65
+ const [input, setInput] = useState(prefillText || '');
66
+ const [composerFocused, setComposerFocused] = useState(false);
67
+ const reduceMotion = useReduceMotion();
68
+ const scrollRef = useRef<ScrollView>(null);
69
+ const typingStopTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
70
+
71
+ useEffect(() => {
72
+ if (prefillText) setInput(prefillText);
73
+ }, [prefillText]);
74
+
75
+ useEffect(
76
+ () => () => {
77
+ if (typingStopTimer.current) clearTimeout(typingStopTimer.current);
78
+ },
79
+ []
80
+ );
81
+
82
+ const scrollToBottom = useCallback((animated = true) => {
83
+ scrollRef.current?.scrollToEnd({ animated });
84
+ }, []);
85
+
86
+ // Land on the newest message the moment the thread opens — notification
87
+ // taps and conversation-list taps both mount this screen. The content-size
88
+ // handler below covers later growth (loads, images, incoming messages).
89
+ useEffect(() => {
90
+ const frame = requestAnimationFrame(() => scrollToBottom(false));
91
+ return () => cancelAnimationFrame(frame);
92
+ }, [scrollToBottom]);
93
+
94
+ // "Load earlier" PREPENDS history; skipping one auto-scroll keeps the reader
95
+ // anchored where they were instead of yanking them back to the bottom.
96
+ const skipNextAutoScroll = useRef(false);
97
+ const handleLoadEarlier = useCallback(() => {
98
+ skipNextAutoScroll.current = true;
99
+ onLoadMore();
100
+ }, [onLoadMore]);
101
+
102
+ const handleContentSizeChange = useCallback(() => {
103
+ if (skipNextAutoScroll.current) {
104
+ skipNextAutoScroll.current = false;
105
+ return;
106
+ }
107
+ scrollToBottom(false);
108
+ }, [scrollToBottom]);
109
+
110
+ // The keyboard shortens the thread instead of scrolling it, so whatever was
111
+ // at the bottom — the newest message, an agent form — would slide in behind
112
+ // the composer. Re-pin on every shrink, unanimated, so the list tracks the
113
+ // keyboard as it rises rather than jumping once it lands.
114
+ const viewportHeight = useRef(0);
115
+ const handleViewportLayout = useCallback(
116
+ (event: LayoutChangeEvent) => {
117
+ const { height } = event.nativeEvent.layout;
118
+ const shrank = viewportHeight.current - height > 1;
119
+ viewportHeight.current = height;
120
+ if (shrank) scrollToBottom(false);
121
+ },
122
+ [scrollToBottom]
123
+ );
124
+
125
+ const canSend = input.trim().length > 0;
126
+
127
+ const handleSend = () => {
128
+ const trimmed = input.trim();
129
+ if (!trimmed) return;
130
+ setInput('');
131
+ if (typingStopTimer.current) clearTimeout(typingStopTimer.current);
132
+ onTyping(false);
133
+ onSendMessage(trimmed);
134
+ scrollToBottom();
135
+ };
136
+
137
+ const handleChangeText = (text: string) => {
138
+ setInput(text);
139
+ if (!text.trim()) return;
140
+ onTyping(true);
141
+ if (typingStopTimer.current) clearTimeout(typingStopTimer.current);
142
+ typingStopTimer.current = setTimeout(() => onTyping(false), 2500);
143
+ };
144
+
145
+ const handleAgentUiAction = useCallback(
146
+ async (action: AgentUiAction) => {
147
+ if (action.type === 'submit_contact' && onIdentifyVisitor) {
148
+ await onIdentifyVisitor({
149
+ email: stringPayload(action.payload.email),
150
+ name:
151
+ stringPayload(action.payload.name) ||
152
+ [stringPayload(action.payload.firstName), stringPayload(action.payload.lastName)]
153
+ .filter(Boolean)
154
+ .join(' ') ||
155
+ undefined,
156
+ phone: stringPayload(action.payload.phone),
157
+ });
158
+ }
159
+ onSendMessage(formatAgentUiActionMessage(action), undefined, action);
160
+ scrollToBottom();
161
+ },
162
+ [onIdentifyVisitor, onSendMessage, scrollToBottom]
163
+ );
164
+
165
+ const headerTextColor = contrastingTextColor(color);
166
+ const onDarkHeader = headerTextColor === palette.white;
167
+ const isEmptyThread = !loading && messages.length === 0;
168
+ // Two stops off the brand color: flat fill looked like an unstyled block.
169
+ // A non-hex brand color falls through mixHex unchanged and renders solid.
170
+ const headerGradient = [color, mixHex(color, '#000000', 0.16)];
171
+ const headerControl = { backgroundColor: withAlpha(headerTextColor, 0.16) };
172
+
173
+ return (
174
+ <View style={globalStyles.screen}>
175
+ {/* One bar instead of a brand band above a separate back row: the band
176
+ was empty whenever no agent was assigned yet. */}
177
+ <GradientBackground
178
+ colors={headerGradient}
179
+ style={[styles.header, { borderBottomColor: mixHex(color, '#000000', 0.26) }]}>
180
+ <Pressable
181
+ onPress={onBack}
182
+ accessibilityRole="button"
183
+ accessibilityLabel={labels.back}
184
+ hitSlop={12}
185
+ style={({ pressed }) => [
186
+ globalStyles.iconButton,
187
+ headerControl,
188
+ pressed && globalStyles.pressed,
189
+ ]}>
190
+ <ChevronIcon size={18} direction={backDirection()} color={headerTextColor} />
191
+ </Pressable>
192
+
193
+ <View style={styles.headerIdentity}>
194
+ <View style={styles.avatarWrapper}>
195
+ <Avatar
196
+ avatar={agentAvatar}
197
+ name={agentName}
198
+ size={36}
199
+ translucent={onDarkHeader}
200
+ color={palette.ink}
201
+ />
202
+ {isOnline ? <View style={styles.onlineDot} /> : null}
203
+ </View>
204
+ <View style={styles.headerText}>
205
+ <Text style={[styles.headerName, { color: headerTextColor }]} numberOfLines={1}>
206
+ {agentName || labels.team}
207
+ </Text>
208
+ <Text
209
+ style={[styles.headerStatus, { color: withAlpha(headerTextColor, 0.75) }]}
210
+ numberOfLines={1}>
211
+ {isOnline ? labels.statusOnline : labels.statusAway}
212
+ </Text>
213
+ </View>
214
+ </View>
215
+
216
+ <Pressable
217
+ onPress={onClose}
218
+ accessibilityRole="button"
219
+ accessibilityLabel={labels.close}
220
+ hitSlop={12}
221
+ style={({ pressed }) => [
222
+ globalStyles.iconButton,
223
+ headerControl,
224
+ pressed && globalStyles.pressed,
225
+ ]}>
226
+ <ChevronIcon size={20} direction="down" color={headerTextColor} />
227
+ </Pressable>
228
+ </GradientBackground>
229
+
230
+ <ScrollView
231
+ ref={scrollRef}
232
+ style={globalStyles.fill}
233
+ contentContainerStyle={[
234
+ styles.messagesContent,
235
+ isEmptyThread && styles.messagesContentEmpty,
236
+ ]}
237
+ keyboardShouldPersistTaps="handled"
238
+ // Interactive dismissal drags the keyboard down under the finger while
239
+ // the composer stays where the last keyboard event left it, which opens
240
+ // a gap under the thread; on-drag hands us a normal hide event instead.
241
+ keyboardDismissMode="on-drag"
242
+ onLayout={handleViewportLayout}
243
+ onContentSizeChange={handleContentSizeChange}>
244
+ {hasMoreMessages ? (
245
+ <Pressable onPress={handleLoadEarlier} accessibilityRole="button" style={styles.loadMore}>
246
+ <Text style={[styles.loadMoreLabel, { color }]}>
247
+ {loading ? labels.loading : labels.loadEarlier}
248
+ </Text>
249
+ </Pressable>
250
+ ) : null}
251
+
252
+ {loading && messages.length === 0 ? (
253
+ <Text style={styles.stateText}>{labels.loading}</Text>
254
+ ) : null}
255
+
256
+ {isEmptyThread ? (
257
+ <View style={styles.emptyState}>
258
+ <View style={[styles.emptyIcon, { backgroundColor: withAlpha(color, 0.08) }]}>
259
+ <ChatOutlineIcon size={28} color={color} />
260
+ </View>
261
+ <Text style={styles.emptyText}>{labels.emptyConversation}</Text>
262
+ </View>
263
+ ) : null}
264
+
265
+ {messages.map((message, index) => {
266
+ const previous = index > 0 ? messages[index - 1] : undefined;
267
+ const startsDay = !previous || !isSameDay(message.createdAt, previous.createdAt);
268
+ const isAgentMessage =
269
+ message.direction !== 'inbound' && message.messageType !== 'system';
270
+ // Name only at the top of a run, the way every messenger does it.
271
+ const startsRun =
272
+ !previous ||
273
+ previous.direction === 'inbound' ||
274
+ previous.messageType === 'system' ||
275
+ previous.senderName !== message.senderName;
276
+
277
+ return (
278
+ <Fragment key={message.id}>
279
+ {startsDay ? (
280
+ <View style={styles.daySeparator}>
281
+ <View style={styles.dayLine} />
282
+ <Text style={styles.dayLabel}>
283
+ {formatDayLabel(message.createdAt, locale, labels)}
284
+ </Text>
285
+ <View style={styles.dayLine} />
286
+ </View>
287
+ ) : null}
288
+ {/* One malformed message (agent blocks, markdown) drops out of
289
+ the thread instead of blanking the whole chat. */}
290
+ <WidgetErrorBoundary fallback={null}>
291
+ <MessageBubble
292
+ message={message}
293
+ color={color}
294
+ labels={labels}
295
+ senderLabel={
296
+ isAgentMessage && (startsRun || startsDay)
297
+ ? message.senderName || agentName || labels.team
298
+ : null
299
+ }
300
+ timeLabel={formatClockTime(message.createdAt, locale)}
301
+ onSubmitProcedureForm={
302
+ shouldShowProcedureForm(messages, message)
303
+ ? (body) => onSendMessage(body)
304
+ : undefined
305
+ }
306
+ onAgentUiAction={
307
+ shouldShowAgentUiBlocks(messages, message) ? handleAgentUiAction : undefined
308
+ }
309
+ />
310
+ </WidgetErrorBoundary>
311
+ </Fragment>
312
+ );
313
+ })}
314
+
315
+ {agentTyping ? (
316
+ <View style={styles.typingRow}>
317
+ <View style={styles.typingBubble}>
318
+ <TypingDot index={0} reduceMotion={reduceMotion} />
319
+ <TypingDot index={1} reduceMotion={reduceMotion} />
320
+ <TypingDot index={2} reduceMotion={reduceMotion} />
321
+ </View>
322
+ <Text style={styles.typingLabel}>{labels.thinking}</Text>
323
+ </View>
324
+ ) : null}
325
+ </ScrollView>
326
+
327
+ {offlineMessage ? (
328
+ <View style={styles.offlineBanner}>
329
+ <Text style={styles.offlineText}>{offlineMessage}</Text>
330
+ </View>
331
+ ) : null}
332
+
333
+ <View style={styles.composerWrapper}>
334
+ <View
335
+ style={[
336
+ styles.composer,
337
+ composerFocused && styles.composerFocused,
338
+ composerFocused && { borderColor: withAlpha(color, 0.55) },
339
+ ]}>
340
+ <TextInput
341
+ value={input}
342
+ onChangeText={handleChangeText}
343
+ onFocus={() => setComposerFocused(true)}
344
+ onBlur={() => setComposerFocused(false)}
345
+ placeholder={labels.messagePlaceholder}
346
+ placeholderTextColor={palette.textSubtle}
347
+ multiline
348
+ style={styles.input}
349
+ accessibilityLabel={labels.messagePlaceholder}
350
+ />
351
+ <Pressable
352
+ onPress={handleSend}
353
+ disabled={!canSend}
354
+ accessibilityRole="button"
355
+ accessibilityState={{ disabled: !canSend }}
356
+ hitSlop={8}
357
+ style={({ pressed }) => [
358
+ styles.sendButton,
359
+ canSend ? { backgroundColor: color } : styles.sendButtonDisabled,
360
+ pressed && canSend && styles.sendButtonPressed,
361
+ ]}>
362
+ <ArrowUpIcon
363
+ size={18}
364
+ color={canSend ? contrastingTextColor(color) : palette.textSubtle}
365
+ />
366
+ </Pressable>
367
+ </View>
368
+ </View>
369
+ </View>
370
+ );
371
+ }
372
+
373
+ function MessageBubble({
374
+ message,
375
+ color,
376
+ labels,
377
+ senderLabel,
378
+ timeLabel,
379
+ onSubmitProcedureForm,
380
+ onAgentUiAction,
381
+ }: {
382
+ message: Message;
383
+ color: string;
384
+ labels: ChatLabels;
385
+ senderLabel?: string | null;
386
+ timeLabel: string;
387
+ onSubmitProcedureForm?: (body: string) => void;
388
+ onAgentUiAction?: (action: AgentUiAction) => void | Promise<void>;
389
+ }) {
390
+ if (message.messageType === 'system') {
391
+ const text = message.bodyPlain || stripHtml(message.body || '') || labels.conversationUpdated;
392
+ return (
393
+ <View style={styles.systemRow}>
394
+ <Text style={styles.systemText}>{text}</Text>
395
+ </View>
396
+ );
397
+ }
398
+
399
+ const isVisitor = message.direction === 'inbound';
400
+ const rtl = isRTL();
401
+ const messageText = message.bodyPlain || stripHtml(message.body || '');
402
+ const hasBody = !!messageText;
403
+ const procedureForm = !isVisitor ? getProcedureForm(message.metadata) : null;
404
+ const agentUiBlocks = !isVisitor ? getAgentUiBlocks(message.metadata) : [];
405
+ const textColor = isVisitor ? palette.white : palette.ink;
406
+
407
+ return (
408
+ <View style={[styles.messageRow, isVisitor ? styles.messageRowRight : styles.messageRowLeft]}>
409
+ {senderLabel ? <Text style={styles.senderLabel}>{senderLabel}</Text> : null}
410
+ <View
411
+ style={[
412
+ styles.bubble,
413
+ isVisitor ? [styles.bubbleVisitor, { backgroundColor: color }] : styles.bubbleAgent,
414
+ rtl && (isVisitor ? styles.bubbleVisitorRTL : styles.bubbleAgentRTL),
415
+ message.status === 'pending' && styles.bubblePending,
416
+ ]}>
417
+ {hasBody ? (
418
+ isVisitor ? (
419
+ <Text style={[styles.bubbleText, { color: textColor }]}>{messageText}</Text>
420
+ ) : (
421
+ <Markdown content={messageText} color={textColor} />
422
+ )
423
+ ) : null}
424
+
425
+ {message.attachments && message.attachments.length > 0 ? (
426
+ <View style={[styles.attachments, hasBody && styles.attachmentsSpaced]}>
427
+ {message.attachments.map((attachment) => (
428
+ <AttachmentPreview
429
+ key={attachment.id}
430
+ attachment={attachment}
431
+ isVisitor={isVisitor}
432
+ />
433
+ ))}
434
+ </View>
435
+ ) : null}
436
+
437
+ {message.status === 'failed' ? (
438
+ <Text
439
+ style={[
440
+ styles.failedText,
441
+ { color: isVisitor ? withAlpha(palette.white, 0.7) : palette.danger },
442
+ ]}>
443
+ {labels.failedToSend}
444
+ </Text>
445
+ ) : null}
446
+
447
+ {timeLabel ? (
448
+ <Text
449
+ style={[
450
+ styles.messageTime,
451
+ { color: isVisitor ? withAlpha(palette.white, 0.7) : palette.textSubtle },
452
+ ]}>
453
+ {timeLabel}
454
+ </Text>
455
+ ) : null}
456
+ </View>
457
+
458
+ {agentUiBlocks.length > 0 && onAgentUiAction ? (
459
+ <AgentUiBlocks
460
+ blocks={agentUiBlocks}
461
+ color={color}
462
+ labels={labels.blocks}
463
+ onAction={onAgentUiAction}
464
+ />
465
+ ) : null}
466
+
467
+ {agentUiBlocks.length === 0 && procedureForm && onSubmitProcedureForm ? (
468
+ <ProcedureFieldForm
469
+ form={procedureForm}
470
+ color={color}
471
+ labels={labels}
472
+ onSubmit={onSubmitProcedureForm}
473
+ />
474
+ ) : null}
475
+ </View>
476
+ );
477
+ }
478
+
479
+ function AttachmentPreview({
480
+ attachment,
481
+ isVisitor,
482
+ }: {
483
+ attachment: WidgetAttachment;
484
+ isVisitor: boolean;
485
+ }) {
486
+ // `url` is empty when the server sent a non-http(s) URL (see resolveUrl).
487
+ const isImage = attachment.contentType?.startsWith('image/') && !!attachment.url;
488
+ const open = () => void openExternalUrl(attachment.url);
489
+
490
+ if (isImage) {
491
+ return (
492
+ <Pressable
493
+ onPress={open}
494
+ accessibilityRole="imagebutton"
495
+ accessibilityLabel={attachment.name}>
496
+ <Image
497
+ source={{ uri: attachment.url }}
498
+ style={styles.attachmentImage}
499
+ resizeMode="cover"
500
+ accessibilityIgnoresInvertColors
501
+ />
502
+ </Pressable>
503
+ );
504
+ }
505
+
506
+ return (
507
+ <Pressable
508
+ onPress={open}
509
+ accessibilityRole="button"
510
+ style={[
511
+ styles.attachmentFile,
512
+ { borderColor: isVisitor ? withAlpha(palette.white, 0.35) : palette.border },
513
+ ]}>
514
+ <Text
515
+ style={[styles.attachmentName, { color: isVisitor ? palette.white : palette.ink }]}
516
+ numberOfLines={1}>
517
+ {attachment.name}
518
+ </Text>
519
+ </Pressable>
520
+ );
521
+ }
522
+
523
+ function ProcedureFieldForm({
524
+ form,
525
+ color,
526
+ labels,
527
+ onSubmit,
528
+ }: {
529
+ form: AgentProcedureForm;
530
+ color: string;
531
+ labels: ChatLabels;
532
+ onSubmit: (body: string) => void;
533
+ }) {
534
+ const [values, setValues] = useState<Record<string, string>>({});
535
+ const [submitted, setSubmitted] = useState(false);
536
+ const [expanded, setExpanded] = useState(false);
537
+
538
+ const canSubmit = form.fields.every((field) => !field.required || values[field.name]?.trim());
539
+
540
+ const handleSubmit = () => {
541
+ if (!canSubmit || submitted) return;
542
+ const body = form.fields
543
+ .map((field) => `${field.label}: ${(values[field.name] || '').trim()}`)
544
+ .filter((line) => !line.endsWith(':'))
545
+ .join('\n');
546
+ if (!body.trim()) return;
547
+ setSubmitted(true);
548
+ onSubmit(body);
549
+ };
550
+
551
+ return (
552
+ <View style={styles.procedureCard}>
553
+ <Pressable
554
+ onPress={() => setExpanded((current) => !current)}
555
+ accessibilityRole="button"
556
+ accessibilityState={{ expanded }}
557
+ style={[styles.procedureToggle, { backgroundColor: expanded ? palette.surface : color }]}>
558
+ <Text
559
+ style={[styles.procedureToggleLabel, { color: expanded ? palette.text : palette.white }]}>
560
+ {submitted ? 'Details sent' : 'Share details'}
561
+ </Text>
562
+ <Text
563
+ style={[styles.procedureToggleHint, { color: expanded ? palette.text : palette.white }]}>
564
+ {expanded ? 'Hide' : `${form.fields.length} field${form.fields.length === 1 ? '' : 's'}`}
565
+ </Text>
566
+ </Pressable>
567
+
568
+ {expanded ? (
569
+ <>
570
+ <Text style={styles.procedureTitle}>{form.procedureName || 'Details'}</Text>
571
+ <View style={styles.procedureFields}>
572
+ {form.fields.map((field) => (
573
+ <View key={field.name}>
574
+ <Text style={styles.procedureLabel}>
575
+ {field.label}
576
+ {field.required ? ' *' : ''}
577
+ </Text>
578
+ <TextInput
579
+ value={values[field.name] || ''}
580
+ onChangeText={(text) =>
581
+ setValues((current) => ({ ...current, [field.name]: text }))
582
+ }
583
+ placeholder={placeholderForField(field)}
584
+ placeholderTextColor={palette.textSubtle}
585
+ editable={!submitted}
586
+ keyboardType={
587
+ field.type === 'email'
588
+ ? 'email-address'
589
+ : field.type === 'phone'
590
+ ? 'phone-pad'
591
+ : 'default'
592
+ }
593
+ autoCapitalize={field.type === 'email' ? 'none' : 'sentences'}
594
+ style={styles.procedureInput}
595
+ />
596
+ </View>
597
+ ))}
598
+ </View>
599
+ <Pressable
600
+ onPress={handleSubmit}
601
+ disabled={!canSubmit || submitted}
602
+ accessibilityRole="button"
603
+ style={[
604
+ styles.procedureSubmit,
605
+ { backgroundColor: canSubmit && !submitted ? color : palette.disabled },
606
+ ]}>
607
+ <Text style={styles.procedureSubmitLabel}>
608
+ {submitted ? labels.procedureSent : labels.procedureSend}
609
+ </Text>
610
+ </Pressable>
611
+ </>
612
+ ) : null}
613
+ </View>
614
+ );
615
+ }
616
+
617
+ function TypingDot({ index, reduceMotion }: { index: number; reduceMotion: boolean | null }) {
618
+ const value = useRef(new Animated.Value(0)).current;
619
+ const animating = reduceMotion === false;
620
+
621
+ useEffect(() => {
622
+ if (!animating) return undefined;
623
+ // Every dot runs a constant 920ms cycle and only the leading delay
624
+ // differs, so the three hold their phase instead of drifting apart.
625
+ const loop = Animated.loop(
626
+ Animated.sequence([
627
+ Animated.delay(index * 140),
628
+ Animated.timing(value, {
629
+ toValue: 1,
630
+ duration: 320,
631
+ easing: Easing.out(Easing.quad),
632
+ useNativeDriver: true,
633
+ }),
634
+ Animated.timing(value, {
635
+ toValue: 0,
636
+ duration: 320,
637
+ easing: Easing.in(Easing.quad),
638
+ useNativeDriver: true,
639
+ }),
640
+ Animated.delay(280 - index * 140),
641
+ ])
642
+ );
643
+ loop.start();
644
+ return () => loop.stop();
645
+ }, [animating, index, value]);
646
+
647
+ const motionStyle = animating
648
+ ? {
649
+ opacity: value.interpolate({ inputRange: [0, 1], outputRange: [0.4, 1] }),
650
+ transform: [
651
+ { translateY: value.interpolate({ inputRange: [0, 1], outputRange: [0, -4] }) },
652
+ ],
653
+ }
654
+ : null;
655
+
656
+ return <Animated.View style={[styles.typingDot, motionStyle]} />;
657
+ }
658
+
659
+ function placeholderForField(field: AgentProcedureForm['fields'][number]) {
660
+ if (field.type === 'email') return 'name@example.com';
661
+ if (field.type === 'phone') return '+962 7...';
662
+ if (field.type === 'date' || field.type === 'time') return '';
663
+ return field.label;
664
+ }
665
+
666
+ function stringPayload(value: unknown) {
667
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
668
+ }
669
+
670
+ function shouldShowProcedureForm(messages: Message[], message: Message) {
671
+ if (message.direction === 'inbound' || !getProcedureForm(message.metadata)) return false;
672
+ return messages.findIndex((candidate) => candidate.id === message.id) === messages.length - 1;
673
+ }
674
+
675
+ /** Hide superseded slot pickers so only the newest one stays actionable. */
676
+ function shouldShowAgentUiBlocks(messages: Message[], message: Message) {
677
+ const blocks = getAgentUiBlocks(message.metadata);
678
+ if (blocks.length === 0) return false;
679
+ if (!blocks.every((block) => block.type === 'appointment_slot_picker')) return true;
680
+
681
+ const messageIndex = messages.findIndex((candidate) => candidate.id === message.id);
682
+ const laterBlockIds = new Set(
683
+ messages
684
+ .slice(messageIndex + 1)
685
+ .flatMap((candidate) => getAgentUiBlocks(candidate.metadata))
686
+ .filter((block) => block.type === 'appointment_slot_picker')
687
+ .map((block) => block.id)
688
+ );
689
+ return blocks.some((block) => !laterBlockIds.has(block.id));
690
+ }