@messenger-box/tailwind-ui-inbox 10.0.3-alpha.100

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 (373) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/LICENSE +21 -0
  3. package/jest.config.js +9 -0
  4. package/lib/cdm-locales/en/translations.json +31 -0
  5. package/lib/cdm-locales/es/translations.json +31 -0
  6. package/lib/components/AIAgent/AIAgent.d.ts +21 -0
  7. package/lib/components/AIAgent/AIAgent.d.ts.map +1 -0
  8. package/lib/components/AIAgent/AIAgent.js +904 -0
  9. package/lib/components/AIAgent/AIAgent.js.map +1 -0
  10. package/lib/components/AIAgent/index.d.ts +2 -0
  11. package/lib/components/AIAgent/index.d.ts.map +1 -0
  12. package/lib/components/InboxMessage/CommonMessage.d.ts +8 -0
  13. package/lib/components/InboxMessage/CommonMessage.d.ts.map +1 -0
  14. package/lib/components/InboxMessage/CommonMessage.js +35 -0
  15. package/lib/components/InboxMessage/CommonMessage.js.map +1 -0
  16. package/lib/components/InboxMessage/ConversationItem.d.ts +14 -0
  17. package/lib/components/InboxMessage/ConversationItem.d.ts.map +1 -0
  18. package/lib/components/InboxMessage/ConversationItem.js +200 -0
  19. package/lib/components/InboxMessage/ConversationItem.js.map +1 -0
  20. package/lib/components/InboxMessage/InputComponent.d.ts +12 -0
  21. package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -0
  22. package/lib/components/InboxMessage/InputComponent.js +359 -0
  23. package/lib/components/InboxMessage/InputComponent.js.map +1 -0
  24. package/lib/components/InboxMessage/LeftSidebar.d.ts +20 -0
  25. package/lib/components/InboxMessage/LeftSidebar.d.ts.map +1 -0
  26. package/lib/components/InboxMessage/LeftSidebar.js +102 -0
  27. package/lib/components/InboxMessage/LeftSidebar.js.map +1 -0
  28. package/lib/components/InboxMessage/MessageInput.d.ts +9 -0
  29. package/lib/components/InboxMessage/MessageInput.d.ts.map +1 -0
  30. package/lib/components/InboxMessage/MessageInput.js +154 -0
  31. package/lib/components/InboxMessage/MessageInput.js.map +1 -0
  32. package/lib/components/InboxMessage/MessageInputComponent.d.ts +9 -0
  33. package/lib/components/InboxMessage/MessageInputComponent.d.ts.map +1 -0
  34. package/lib/components/InboxMessage/Messages.d.ts +17 -0
  35. package/lib/components/InboxMessage/Messages.d.ts.map +1 -0
  36. package/lib/components/InboxMessage/Messages.js +99 -0
  37. package/lib/components/InboxMessage/Messages.js.map +1 -0
  38. package/lib/components/InboxMessage/MessagesBuilderUi.d.ts +17 -0
  39. package/lib/components/InboxMessage/MessagesBuilderUi.d.ts.map +1 -0
  40. package/lib/components/InboxMessage/Popover.d.ts +3 -0
  41. package/lib/components/InboxMessage/Popover.d.ts.map +1 -0
  42. package/lib/components/InboxMessage/Popover.js +31 -0
  43. package/lib/components/InboxMessage/Popover.js.map +1 -0
  44. package/lib/components/InboxMessage/RightSidebar.d.ts +9 -0
  45. package/lib/components/InboxMessage/RightSidebar.d.ts.map +1 -0
  46. package/lib/components/InboxMessage/RightSidebar.js +9 -0
  47. package/lib/components/InboxMessage/RightSidebar.js.map +1 -0
  48. package/lib/components/InboxMessage/RightSidebarAi.d.ts +23 -0
  49. package/lib/components/InboxMessage/RightSidebarAi.d.ts.map +1 -0
  50. package/lib/components/InboxMessage/RightSidebarAi.js +9 -0
  51. package/lib/components/InboxMessage/RightSidebarAi.js.map +1 -0
  52. package/lib/components/InboxMessage/ServiceConversationItem.d.ts +12 -0
  53. package/lib/components/InboxMessage/ServiceConversationItem.d.ts.map +1 -0
  54. package/lib/components/InboxMessage/ServiceConversationItem.js +185 -0
  55. package/lib/components/InboxMessage/ServiceConversationItem.js.map +1 -0
  56. package/lib/components/InboxMessage/ServiceInboxItem.d.ts +12 -0
  57. package/lib/components/InboxMessage/ServiceInboxItem.d.ts.map +1 -0
  58. package/lib/components/InboxMessage/ServiceInboxItem.js +182 -0
  59. package/lib/components/InboxMessage/ServiceInboxItem.js.map +1 -0
  60. package/lib/components/InboxMessage/SubscriptionHandler.d.ts +19 -0
  61. package/lib/components/InboxMessage/SubscriptionHandler.d.ts.map +1 -0
  62. package/lib/components/InboxMessage/SubscriptionHandler.js +41 -0
  63. package/lib/components/InboxMessage/SubscriptionHandler.js.map +1 -0
  64. package/lib/components/InboxMessage/UploadImageButton.d.ts +7 -0
  65. package/lib/components/InboxMessage/UploadImageButton.d.ts.map +1 -0
  66. package/lib/components/InboxMessage/UploadImageButton.js +34 -0
  67. package/lib/components/InboxMessage/UploadImageButton.js.map +1 -0
  68. package/lib/components/InboxMessage/UserModalContent.d.ts +3 -0
  69. package/lib/components/InboxMessage/UserModalContent.d.ts.map +1 -0
  70. package/lib/components/InboxMessage/UserModalContent.js +60 -0
  71. package/lib/components/InboxMessage/UserModalContent.js.map +1 -0
  72. package/lib/components/InboxMessage/index.d.ts +17 -0
  73. package/lib/components/InboxMessage/index.d.ts.map +1 -0
  74. package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts +11 -0
  75. package/lib/components/InboxMessage/message-widgets/CommonMessage.d.ts.map +1 -0
  76. package/lib/components/InboxMessage/message-widgets/CommonMessage.js +44 -0
  77. package/lib/components/InboxMessage/message-widgets/CommonMessage.js.map +1 -0
  78. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts +11 -0
  79. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts.map +1 -0
  80. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js +194 -0
  81. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js.map +1 -0
  82. package/lib/components/InboxMessage/message-widgets/MessageCard.d.ts +8 -0
  83. package/lib/components/InboxMessage/message-widgets/MessageCard.d.ts.map +1 -0
  84. package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.d.ts +12 -0
  85. package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.d.ts.map +1 -0
  86. package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.js +37 -0
  87. package/lib/components/InboxMessage/message-widgets/MessageSliceRenderer.js.map +1 -0
  88. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +18 -0
  89. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -0
  90. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +1082 -0
  91. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -0
  92. package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts +8 -0
  93. package/lib/components/InboxMessage/message-widgets/PlainMessage.d.ts.map +1 -0
  94. package/lib/components/InboxMessage/message-widgets/PlainMessage.js +14 -0
  95. package/lib/components/InboxMessage/message-widgets/PlainMessage.js.map +1 -0
  96. package/lib/components/InboxMessage/message-widgets/PropertyMessageWidget.d.ts +9 -0
  97. package/lib/components/InboxMessage/message-widgets/PropertyMessageWidget.d.ts.map +1 -0
  98. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts +14 -0
  99. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.d.ts.map +1 -0
  100. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js +333 -0
  101. package/lib/components/InboxMessage/message-widgets/SlackLikeMessageGroup.js.map +1 -0
  102. package/lib/components/InboxMessage/message-widgets/index.d.ts +4 -0
  103. package/lib/components/InboxMessage/message-widgets/index.d.ts.map +1 -0
  104. package/lib/components/ModelConfigPanel.d.ts +37 -0
  105. package/lib/components/ModelConfigPanel.d.ts.map +1 -0
  106. package/lib/components/ModelConfigPanel.js +317 -0
  107. package/lib/components/ModelConfigPanel.js.map +1 -0
  108. package/lib/components/filler-components/RightSiderBar.d.ts +24 -0
  109. package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -0
  110. package/lib/components/filler-components/RightSiderBar.js +335 -0
  111. package/lib/components/filler-components/RightSiderBar.js.map +1 -0
  112. package/lib/components/inbox/FilesList.d.ts +20 -0
  113. package/lib/components/inbox/FilesList.d.ts.map +1 -0
  114. package/lib/components/inbox/FilesList.js +68 -0
  115. package/lib/components/inbox/FilesList.js.map +1 -0
  116. package/lib/components/inbox/MessageItem.d.ts +17 -0
  117. package/lib/components/inbox/MessageItem.d.ts.map +1 -0
  118. package/lib/components/inbox/MessageItem.js +50 -0
  119. package/lib/components/inbox/MessageItem.js.map +1 -0
  120. package/lib/components/inbox/ThreadItem.d.ts +11 -0
  121. package/lib/components/inbox/ThreadItem.d.ts.map +1 -0
  122. package/lib/components/inbox/ThreadItem.js +147 -0
  123. package/lib/components/inbox/ThreadItem.js.map +1 -0
  124. package/lib/components/inbox/index.d.ts +4 -0
  125. package/lib/components/inbox/index.d.ts.map +1 -0
  126. package/lib/components/index.d.ts +7 -0
  127. package/lib/components/index.d.ts.map +1 -0
  128. package/lib/components/live-code-editor/hybrid-live-editor.d.ts +20 -0
  129. package/lib/components/live-code-editor/hybrid-live-editor.d.ts.map +1 -0
  130. package/lib/components/live-code-editor/hybrid-live-editor.js +68 -0
  131. package/lib/components/live-code-editor/hybrid-live-editor.js.map +1 -0
  132. package/lib/components/live-code-editor/index.d.ts +4 -0
  133. package/lib/components/live-code-editor/index.d.ts.map +1 -0
  134. package/lib/components/live-code-editor/live-code-editor.d.ts +14 -0
  135. package/lib/components/live-code-editor/live-code-editor.d.ts.map +1 -0
  136. package/lib/components/live-code-editor/live-code-editor.js +207 -0
  137. package/lib/components/live-code-editor/live-code-editor.js.map +1 -0
  138. package/lib/components/slot-fill/chat-message-filler.d.ts +4 -0
  139. package/lib/components/slot-fill/chat-message-filler.d.ts.map +1 -0
  140. package/lib/components/slot-fill/chat-message-filler.js +5 -0
  141. package/lib/components/slot-fill/chat-message-filler.js.map +1 -0
  142. package/lib/components/slot-fill/chat-message-slot.d.ts +11 -0
  143. package/lib/components/slot-fill/chat-message-slot.d.ts.map +1 -0
  144. package/lib/components/slot-fill/chat-message-slot.js +6 -0
  145. package/lib/components/slot-fill/chat-message-slot.js.map +1 -0
  146. package/lib/components/slot-fill/index.d.ts +4 -0
  147. package/lib/components/slot-fill/index.d.ts.map +1 -0
  148. package/lib/components/slot-fill/right-sidebar-filler.d.ts +4 -0
  149. package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -0
  150. package/lib/components/slot-fill/right-sidebar-filler.js +13 -0
  151. package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -0
  152. package/lib/components/ui/button.d.ts +9 -0
  153. package/lib/components/ui/button.d.ts.map +1 -0
  154. package/lib/compute.d.ts +8 -0
  155. package/lib/compute.d.ts.map +1 -0
  156. package/lib/compute.js +137 -0
  157. package/lib/compute.js.map +1 -0
  158. package/lib/config/env-config.d.ts +13 -0
  159. package/lib/config/env-config.d.ts.map +1 -0
  160. package/lib/config/env-config.js +34 -0
  161. package/lib/config/env-config.js.map +1 -0
  162. package/lib/config/index.d.ts +2 -0
  163. package/lib/config/index.d.ts.map +1 -0
  164. package/lib/constants/breakpoints.d.ts +8 -0
  165. package/lib/constants/breakpoints.d.ts.map +1 -0
  166. package/lib/constants/index.d.ts +3 -0
  167. package/lib/constants/index.d.ts.map +1 -0
  168. package/lib/container/AiInbox.d.ts +15 -0
  169. package/lib/container/AiInbox.d.ts.map +1 -0
  170. package/lib/container/AiInboxWithLoader.d.ts +36 -0
  171. package/lib/container/AiInboxWithLoader.d.ts.map +1 -0
  172. package/lib/container/AiLandingInput.d.ts +4 -0
  173. package/lib/container/AiLandingInput.d.ts.map +1 -0
  174. package/lib/container/AiLandingInput.js +101 -0
  175. package/lib/container/AiLandingInput.js.map +1 -0
  176. package/lib/container/Inbox.d.ts +15 -0
  177. package/lib/container/Inbox.d.ts.map +1 -0
  178. package/lib/container/Inbox.js +955 -0
  179. package/lib/container/Inbox.js.map +1 -0
  180. package/lib/container/InboxAiMessagesLoader.d.ts +15 -0
  181. package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -0
  182. package/lib/container/InboxAiMessagesLoader.js +30 -0
  183. package/lib/container/InboxAiMessagesLoader.js.map +1 -0
  184. package/lib/container/InboxContainer.d.ts +12 -0
  185. package/lib/container/InboxContainer.d.ts.map +1 -0
  186. package/lib/container/InboxContainer.js +31 -0
  187. package/lib/container/InboxContainer.js.map +1 -0
  188. package/lib/container/InboxTemplate1.d.ts +15 -0
  189. package/lib/container/InboxTemplate1.d.ts.map +1 -0
  190. package/lib/container/InboxTemplate1WithLoader.d.ts +36 -0
  191. package/lib/container/InboxTemplate1WithLoader.d.ts.map +1 -0
  192. package/lib/container/InboxTemplate2.d.ts +15 -0
  193. package/lib/container/InboxTemplate2.d.ts.map +1 -0
  194. package/lib/container/InboxWithAiLoader.d.ts +15 -0
  195. package/lib/container/InboxWithAiLoader.d.ts.map +1 -0
  196. package/lib/container/InboxWithAiLoader.js +56 -0
  197. package/lib/container/InboxWithAiLoader.js.map +1 -0
  198. package/lib/container/InboxWithLoader.d.ts +36 -0
  199. package/lib/container/InboxWithLoader.d.ts.map +1 -0
  200. package/lib/container/InboxWithLoader.js +277 -0
  201. package/lib/container/InboxWithLoader.js.map +1 -0
  202. package/lib/container/ServiceInbox.d.ts +9 -0
  203. package/lib/container/ServiceInbox.d.ts.map +1 -0
  204. package/lib/container/ServiceInbox.js +144 -0
  205. package/lib/container/ServiceInbox.js.map +1 -0
  206. package/lib/container/ThreadMessages.d.ts +13 -0
  207. package/lib/container/ThreadMessages.d.ts.map +1 -0
  208. package/lib/container/ThreadMessages.js +314 -0
  209. package/lib/container/ThreadMessages.js.map +1 -0
  210. package/lib/container/ThreadMessagesInbox.d.ts +14 -0
  211. package/lib/container/ThreadMessagesInbox.d.ts.map +1 -0
  212. package/lib/container/ThreadMessagesInbox.js +341 -0
  213. package/lib/container/ThreadMessagesInbox.js.map +1 -0
  214. package/lib/container/Threads.d.ts +8 -0
  215. package/lib/container/Threads.d.ts.map +1 -0
  216. package/lib/container/Threads.js +231 -0
  217. package/lib/container/Threads.js.map +1 -0
  218. package/lib/container/ThreadsInbox.d.ts +21 -0
  219. package/lib/container/ThreadsInbox.d.ts.map +1 -0
  220. package/lib/container/ThreadsInbox.js +244 -0
  221. package/lib/container/ThreadsInbox.js.map +1 -0
  222. package/lib/container/apply-footer-styles.d.ts +2 -0
  223. package/lib/container/apply-footer-styles.d.ts.map +1 -0
  224. package/lib/container/apply-footer-styles.js +16 -0
  225. package/lib/container/apply-footer-styles.js.map +1 -0
  226. package/lib/container/index.d.ts +13 -0
  227. package/lib/container/index.d.ts.map +1 -0
  228. package/lib/enums/index.d.ts +2 -0
  229. package/lib/enums/index.d.ts.map +1 -0
  230. package/lib/enums/messenger-slot-fill-name-enum.d.ts +5 -0
  231. package/lib/enums/messenger-slot-fill-name-enum.d.ts.map +1 -0
  232. package/lib/enums/messenger-slot-fill-name-enum.js +5 -0
  233. package/lib/enums/messenger-slot-fill-name-enum.js.map +1 -0
  234. package/lib/hooks/index.d.ts +3 -0
  235. package/lib/hooks/index.d.ts.map +1 -0
  236. package/lib/hooks/use-file-sync.d.ts +16 -0
  237. package/lib/hooks/use-file-sync.d.ts.map +1 -0
  238. package/lib/hooks/use-file-sync.js +63 -0
  239. package/lib/hooks/use-file-sync.js.map +1 -0
  240. package/lib/hooks/usePersistentModelConfig.d.ts +15 -0
  241. package/lib/hooks/usePersistentModelConfig.d.ts.map +1 -0
  242. package/lib/hooks/usePersistentModelConfig.js +46 -0
  243. package/lib/hooks/usePersistentModelConfig.js.map +1 -0
  244. package/lib/index.d.ts +10 -0
  245. package/lib/index.d.ts.map +1 -0
  246. package/lib/index.js +1 -0
  247. package/lib/index.js.map +1 -0
  248. package/lib/interfaces/index.d.ts +2 -0
  249. package/lib/interfaces/index.d.ts.map +1 -0
  250. package/lib/interfaces/message-widgets.interface.d.ts +21 -0
  251. package/lib/interfaces/message-widgets.interface.d.ts.map +1 -0
  252. package/lib/machines/aiAgentMachine.d.ts +3 -0
  253. package/lib/machines/aiAgentMachine.d.ts.map +1 -0
  254. package/lib/machines/aiAgentMachine.js +1083 -0
  255. package/lib/machines/aiAgentMachine.js.map +1 -0
  256. package/lib/machines/aiAgentMachine.simple.d.ts +3 -0
  257. package/lib/machines/aiAgentMachine.simple.d.ts.map +1 -0
  258. package/lib/machines/aiAgentMachine.simple.js +108 -0
  259. package/lib/machines/aiAgentMachine.simple.js.map +1 -0
  260. package/lib/machines/index.d.ts +3 -0
  261. package/lib/machines/index.d.ts.map +1 -0
  262. package/lib/machines/types.d.ts +77 -0
  263. package/lib/machines/types.d.ts.map +1 -0
  264. package/lib/module.d.ts +7 -0
  265. package/lib/module.d.ts.map +1 -0
  266. package/lib/module.js +26 -0
  267. package/lib/module.js.map +1 -0
  268. package/lib/routes.json +98 -0
  269. package/lib/styles/responsive.css +76 -0
  270. package/lib/templates/InboxWithAi.d.ts +15 -0
  271. package/lib/templates/InboxWithAi.d.ts.map +1 -0
  272. package/lib/templates/InboxWithAi.js +440 -0
  273. package/lib/templates/InboxWithAi.js.map +1 -0
  274. package/lib/templates/InboxWithAi.tsx +533 -0
  275. package/lib/templates/index.d.ts +2 -0
  276. package/lib/templates/index.d.ts.map +1 -0
  277. package/lib/templates/index.ts +1 -0
  278. package/lib/utils/utils.d.ts +2 -0
  279. package/lib/utils/utils.d.ts.map +1 -0
  280. package/lib/utils/utils.js +3 -0
  281. package/lib/utils/utils.js.map +1 -0
  282. package/package.json +62 -0
  283. package/rollup.config.mjs +35 -0
  284. package/src/cdm-locales/en/translations.json +31 -0
  285. package/src/cdm-locales/es/translations.json +31 -0
  286. package/src/components/AIAgent/AIAgent.tsx +1103 -0
  287. package/src/components/AIAgent/AIAgent.tsx.bk +1365 -0
  288. package/src/components/AIAgent/README.md +82 -0
  289. package/src/components/AIAgent/index.ts +1 -0
  290. package/src/components/InboxMessage/CommonMessage.tsx +40 -0
  291. package/src/components/InboxMessage/ConversationItem.tsx +255 -0
  292. package/src/components/InboxMessage/InputComponent.tsx +462 -0
  293. package/src/components/InboxMessage/LeftSidebar.tsx +140 -0
  294. package/src/components/InboxMessage/MessageInput.tsx +209 -0
  295. package/src/components/InboxMessage/MessageInputComponent.tsx +245 -0
  296. package/src/components/InboxMessage/Messages.tsx +137 -0
  297. package/src/components/InboxMessage/MessagesBuilderUi.tsx +205 -0
  298. package/src/components/InboxMessage/Popover.tsx +42 -0
  299. package/src/components/InboxMessage/RightSidebar.tsx +22 -0
  300. package/src/components/InboxMessage/RightSidebarAi.tsx +37 -0
  301. package/src/components/InboxMessage/ServiceConversationItem.tsx +234 -0
  302. package/src/components/InboxMessage/ServiceInboxItem.tsx +223 -0
  303. package/src/components/InboxMessage/SubscriptionHandler.tsx +55 -0
  304. package/src/components/InboxMessage/UploadImageButton.tsx +46 -0
  305. package/src/components/InboxMessage/UserModalContent.tsx +60 -0
  306. package/src/components/InboxMessage/index.ts +16 -0
  307. package/src/components/InboxMessage/message-widgets/CommonMessage.tsx +69 -0
  308. package/src/components/InboxMessage/message-widgets/ErrorFixCard.tsx +240 -0
  309. package/src/components/InboxMessage/message-widgets/MessageCard.tsx +127 -0
  310. package/src/components/InboxMessage/message-widgets/MessageSliceRenderer.tsx +40 -0
  311. package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +1295 -0
  312. package/src/components/InboxMessage/message-widgets/PlainMessage.tsx +18 -0
  313. package/src/components/InboxMessage/message-widgets/PropertyMessageWidget.tsx +29 -0
  314. package/src/components/InboxMessage/message-widgets/SlackLikeMessageGroup.tsx +492 -0
  315. package/src/components/InboxMessage/message-widgets/index.ts +3 -0
  316. package/src/components/ModelConfigPanel.tsx +345 -0
  317. package/src/components/filler-components/RightSiderBar.tsx +408 -0
  318. package/src/components/inbox/FilesList.tsx +89 -0
  319. package/src/components/inbox/MessageItem.tsx +50 -0
  320. package/src/components/inbox/ThreadItem.tsx +295 -0
  321. package/src/components/inbox/index.ts +3 -0
  322. package/src/components/index.ts +22 -0
  323. package/src/components/live-code-editor/hybrid-live-editor.tsx +105 -0
  324. package/src/components/live-code-editor/index.ts +3 -0
  325. package/src/components/live-code-editor/live-code-editor.tsx +257 -0
  326. package/src/components/slot-fill/chat-message-filler.tsx +18 -0
  327. package/src/components/slot-fill/chat-message-slot.tsx +18 -0
  328. package/src/components/slot-fill/index.ts +3 -0
  329. package/src/components/slot-fill/right-sidebar-filler.tsx +39 -0
  330. package/src/components/ui/button.tsx +32 -0
  331. package/src/compute.ts +134 -0
  332. package/src/config/env-config.ts +17 -0
  333. package/src/config/index.ts +1 -0
  334. package/src/constants/breakpoints.ts +7 -0
  335. package/src/constants/index.ts +5 -0
  336. package/src/container/AiInbox.tsx +1819 -0
  337. package/src/container/AiInboxWithLoader.tsx +356 -0
  338. package/src/container/AiLandingInput.tsx +221 -0
  339. package/src/container/Inbox.tsx +1092 -0
  340. package/src/container/InboxAiMessagesLoader.tsx +44 -0
  341. package/src/container/InboxContainer.tsx +35 -0
  342. package/src/container/InboxTemplate1.tsx +1542 -0
  343. package/src/container/InboxTemplate1WithLoader.tsx +338 -0
  344. package/src/container/InboxTemplate2.tsx +1606 -0
  345. package/src/container/InboxWithAiLoader.tsx +76 -0
  346. package/src/container/InboxWithLoader.tsx +341 -0
  347. package/src/container/ServiceInbox.tsx +190 -0
  348. package/src/container/ThreadMessages.tsx +371 -0
  349. package/src/container/ThreadMessagesInbox.tsx +450 -0
  350. package/src/container/Threads.tsx +270 -0
  351. package/src/container/ThreadsInbox.tsx +354 -0
  352. package/src/container/apply-footer-styles.ts +17 -0
  353. package/src/container/index.ts +31 -0
  354. package/src/enums/index.ts +1 -0
  355. package/src/enums/messenger-slot-fill-name-enum.ts +4 -0
  356. package/src/hooks/index.ts +2 -0
  357. package/src/hooks/use-file-sync.ts +91 -0
  358. package/src/hooks/usePersistentModelConfig.ts +63 -0
  359. package/src/index.ts +37 -0
  360. package/src/interfaces/index.ts +1 -0
  361. package/src/interfaces/message-widgets.interface.ts +21 -0
  362. package/src/machines/aiAgentMachine.simple.ts +89 -0
  363. package/src/machines/aiAgentMachine.ts +1296 -0
  364. package/src/machines/aiAgentMachine.ts.bk +1296 -0
  365. package/src/machines/index.ts +2 -0
  366. package/src/machines/types.ts +59 -0
  367. package/src/module.tsx +32 -0
  368. package/src/styles/responsive.css +76 -0
  369. package/src/templates/InboxWithAi.tsx +533 -0
  370. package/src/templates/index.ts +1 -0
  371. package/src/utils/utils.ts +3 -0
  372. package/tsconfig.json +14 -0
  373. package/webpack.config.js +92 -0
@@ -0,0 +1,1092 @@
1
+ import React, { ReactNode, useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
2
+ import { orderBy, uniqBy } from 'lodash-es';
3
+ import {
4
+ OnChatMessageAddedDocument as CHAT_MESSAGE_ADDED,
5
+ useSendMessagesMutation,
6
+ GetChannelsByUserDocument,
7
+ MessagesDocument,
8
+ } from 'common/graphql';
9
+ import { useUploadFiles } from '@messenger-box/platform-client';
10
+ import { IFileInfo, RoomType, PostTypeEnum } from 'common';
11
+ import { useSelector, shallowEqual } from 'react-redux';
12
+ import { useNavigate, useParams } from '@remix-run/react';
13
+ import { LeftSidebar, MessageInput, Messages, RightSidebar } from '../components';
14
+ import { Store, userSelector } from '@adminide-stack/user-auth0-client';
15
+ import { IUserState } from '@adminide-stack/core';
16
+ import { config } from '../config';
17
+ import { applyFooterStyles } from './apply-footer-styles';
18
+ import { objectId } from '@messenger-box/core';
19
+ import { ThreadsInbox } from './ThreadsInbox';
20
+ import { ThreadMessagesInbox } from './ThreadMessagesInbox';
21
+ import { useApolloClient } from '@apollo/client';
22
+ import { SubscriptionHandler } from '../components/InboxMessage/SubscriptionHandler';
23
+
24
+ const { MESSAGES_PER_PAGE } = config;
25
+
26
+ // Types
27
+ interface DrawerProps {
28
+ isOpen: boolean;
29
+ onClose: () => void;
30
+ children: ReactNode;
31
+ title?: string;
32
+ }
33
+
34
+ export interface InboxProps {
35
+ channelFilters?: Record<string, unknown>;
36
+ channelRole?: string;
37
+ supportServices?: boolean;
38
+ pathPrefix?: string;
39
+ data?: any;
40
+ orgName?: string;
41
+ }
42
+
43
+ interface MobilePreviewState {
44
+ mobilePreviewVisibility: boolean;
45
+ mobilePreviewText: string | ReactNode;
46
+ mobilePreviewCTAText: string | ReactNode;
47
+ }
48
+
49
+ // Static utility hooks and components
50
+ const useMediaQuery = (query: string) => {
51
+ const [matches, setMatches] = React.useState(false);
52
+
53
+ useEffect(() => {
54
+ if (typeof window === 'undefined') return;
55
+
56
+ const mediaQuery = window.matchMedia(query);
57
+ const updateMatches = () => setMatches(mediaQuery.matches);
58
+
59
+ updateMatches();
60
+ mediaQuery.addEventListener('change', updateMatches);
61
+ return () => mediaQuery.removeEventListener('change', updateMatches);
62
+ }, [query]);
63
+
64
+ return matches;
65
+ };
66
+
67
+ // Hook to get window dimensions
68
+ const useWindowDimensions = () => {
69
+ const [windowDimensions, setWindowDimensions] = React.useState({
70
+ width: typeof window !== 'undefined' ? window.innerWidth : 1024,
71
+ height: typeof window !== 'undefined' ? window.innerHeight : 768,
72
+ });
73
+
74
+ useEffect(() => {
75
+ if (typeof window === 'undefined') return;
76
+
77
+ const handleResize = () => {
78
+ setWindowDimensions({
79
+ width: window.innerWidth,
80
+ height: window.innerHeight,
81
+ });
82
+ };
83
+
84
+ window.addEventListener('resize', handleResize);
85
+ handleResize(); // Set initial dimensions
86
+
87
+ return () => window.removeEventListener('resize', handleResize);
88
+ }, []);
89
+
90
+ return windowDimensions;
91
+ };
92
+
93
+ // Static components
94
+ const Spinner = React.memo(({ className = '' }: { className?: string }) => (
95
+ <div className={`animate-spin rounded-full border-4 border-gray-200 border-t-blue-500 ${className}`}>
96
+ <span className="sr-only">Loading...</span>
97
+ </div>
98
+ ));
99
+
100
+ const Drawer = React.memo(({ isOpen, onClose, children, title }: DrawerProps) => {
101
+ if (!isOpen) return null;
102
+
103
+ return (
104
+ <div className="fixed inset-0 z-50 overflow-hidden">
105
+ <div className="absolute inset-0 bg-black bg-opacity-50" onClick={onClose} />
106
+ <div className="absolute bottom-0 left-0 right-0 bg-white rounded-t-lg shadow-lg max-h-[80vh] flex flex-col overflow-hidden">
107
+ <div className="flex items-center justify-between p-4 border-b border-gray-200 flex-shrink-0">
108
+ <h2 className="text-lg font-semibold truncate">{title}</h2>
109
+ <button
110
+ onClick={onClose}
111
+ className="p-1 hover:bg-gray-100 rounded-full transition-colors flex-shrink-0 ml-2"
112
+ >
113
+ <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
114
+ <path
115
+ strokeLinecap="round"
116
+ strokeLinejoin="round"
117
+ strokeWidth={2}
118
+ d="M6 18L18 6M6 6l12 12"
119
+ />
120
+ </svg>
121
+ </button>
122
+ </div>
123
+ <div className="flex-1 p-4 overflow-y-auto" style={{ minHeight: 0 }}>
124
+ {children}
125
+ </div>
126
+ </div>
127
+ </div>
128
+ );
129
+ });
130
+
131
+ const EmptyState = React.memo(() => (
132
+ <div className="h-full flex items-center justify-center bg-gray-100 p-4 sm:p-6 overflow-hidden">
133
+ <div className="text-center max-w-sm mx-auto">
134
+ <div className="text-3xl sm:text-4xl text-gray-400 mb-4">💬</div>
135
+ <h3 className="text-lg sm:text-xl font-semibold text-gray-600 mb-2">Welcome to Messenger</h3>
136
+ <p className="text-sm sm:text-base text-gray-500 leading-relaxed">
137
+ Select a conversation from the sidebar to start messaging
138
+ </p>
139
+ </div>
140
+ </div>
141
+ ));
142
+
143
+ // Mobile preview reducer
144
+ const mobilePreviewReducer = (
145
+ state: MobilePreviewState,
146
+ action: { payload: Partial<MobilePreviewState>; type: string },
147
+ ) => {
148
+ if (action.type === 'update') {
149
+ return { ...state, ...action.payload };
150
+ }
151
+ return state;
152
+ };
153
+
154
+ const Inbox = (props: InboxProps) => {
155
+ const { channelFilters: channelFilterProp, channelRole, supportServices, data, orgName, pathPrefix = null } = props;
156
+ const { id: pathChannelId, postId: pathPostId } = useParams();
157
+ const navigate = useNavigate();
158
+ const apolloClient = useApolloClient();
159
+
160
+ // Reduced state - only UI state remains, data comes from Apollo cache
161
+ const [isBottomDrawerOpen, setBottomDrawer] = React.useState(false);
162
+ const [mobilePreviewState, localDispatch] = useReducer(mobilePreviewReducer, {
163
+ mobilePreviewVisibility: false,
164
+ mobilePreviewText: false,
165
+ mobilePreviewCTAText: false,
166
+ });
167
+
168
+ // Hooks - improved responsive breakpoints with better granularity
169
+ const { width: windowWidth, height: windowHeight } = useWindowDimensions();
170
+ const isMobileView = useMediaQuery('(max-width: 640px)');
171
+ const isSmallTabletView = useMediaQuery('(min-width: 641px) and (max-width: 900px)');
172
+ const isTabletView = useMediaQuery('(min-width: 901px) and (max-width: 1024px)');
173
+ const isDesktopView = useMediaQuery('(min-width: 1025px)');
174
+ const isLargeDesktopView = useMediaQuery('(min-width: 1440px)');
175
+ const isSmallScreen = useMediaQuery('(max-width: 900px)');
176
+ // const auth = useSelector(userSelector);
177
+ const auth: any = useSelector<Store.Auth, IUserState>(userSelector, shallowEqual);
178
+ // const user = useSelector((state: any) => state.user, shallowEqual);
179
+
180
+ // Data destructuring from Apollo queries
181
+ const GetChannelsByUserQuery = data?.[0];
182
+ const {
183
+ data: userChannels,
184
+ loading: userChannelsLoading,
185
+ refetch: getChannelsRefetch,
186
+ } = GetChannelsByUserQuery || {};
187
+
188
+ // Get data directly from Apollo cache instead of local state
189
+ const channels = useMemo(() => {
190
+ if (!userChannels?.channelsByUser && !userChannels?.supportServiceChannels) return [];
191
+
192
+ return uniqBy([...(userChannels?.supportServiceChannels ?? []), ...(userChannels?.channelsByUser ?? [])], 'id');
193
+ }, [userChannels]);
194
+
195
+ // Memoize stable channel array to prevent unnecessary re-renders
196
+ const stableChannels = useMemo(() => {
197
+ return channels || [];
198
+ }, [channels]);
199
+
200
+ // Memoized values derived from Apollo cache data
201
+ const channelFilters = useMemo(() => {
202
+ const filters = { ...channelFilterProp };
203
+ const channelType = filters?.type ?? RoomType.Direct;
204
+ filters.type = supportServices ? [channelType, RoomType.Service] : channelType;
205
+ return filters;
206
+ }, [channelFilterProp, supportServices]);
207
+
208
+ const users = useMemo(() => {
209
+ return (
210
+ channels?.reduce((acc, curr) => {
211
+ const newMembers = curr.members?.filter(({ user }) => !acc.find(({ id }) => id === user.id)) || [];
212
+ return [...acc, ...newMembers.map(({ user }) => user)];
213
+ }, []) || []
214
+ );
215
+ }, [channels]);
216
+
217
+ // const currentUser = useMemo(
218
+ // () => users?.find((user) => user && user.alias?.includes(auth?.authUserId)),
219
+ // [users, auth?.authUserId],
220
+ // );
221
+ const currentUser = auth;
222
+
223
+ const channelName = useMemo(() => {
224
+ if (!channels || !pathChannelId) return '';
225
+
226
+ const currChannel = channels?.find((ch) => ch.id === pathChannelId);
227
+ if (!currChannel) return '';
228
+
229
+ const { members, title, type } = currChannel;
230
+
231
+ if (type === RoomType.Direct && members?.length >= 2) {
232
+ const otherUser = members.find((member) => member.user.id !== currentUser?.id);
233
+ if (otherUser?.user) {
234
+ const { givenName, familyName } = otherUser.user;
235
+ if (givenName && familyName) return `${givenName} ${familyName}`;
236
+ return givenName || familyName || title || 'Direct Message';
237
+ }
238
+ return title || 'Direct Message';
239
+ }
240
+
241
+ if (type === RoomType.Direct && members?.length === 1) {
242
+ if (members[0].user?.givenName && members[0]?.user?.familyName) {
243
+ return `${members[0].user?.givenName} ${members[0].user?.familyName}`;
244
+ }
245
+ return members[0].user?.givenName || members[0].user?.familyName || 'Direct Message';
246
+ }
247
+
248
+ return title || 'Channel';
249
+ }, [channels, pathChannelId, currentUser]);
250
+
251
+ // Effects
252
+ useEffect(() => {
253
+ applyFooterStyles();
254
+
255
+ // Optimistic refetch with cache update
256
+ const timeout = setTimeout(() => {
257
+ getChannelsRefetch?.({
258
+ role: channelRole,
259
+ criteria: orgName
260
+ ? { ...channelFilters, orgName: channelFilters?.orgName || orgName || '' }
261
+ : channelFilters,
262
+ supportServices: !!supportServices,
263
+ supportServiceCriteria: { type: RoomType.Service },
264
+ });
265
+ }, 0);
266
+ return () => clearTimeout(timeout);
267
+ }, [channelRole, channelFilters, supportServices, getChannelsRefetch]);
268
+
269
+ // Optimistic navigation with cache updates
270
+ const handleSelectChannel = useCallback(
271
+ async (channelId: string, pId: string | null = null) => {
272
+ // Optimistic UI update
273
+ const mainPath = orgName
274
+ ? pId
275
+ ? `/o/${orgName}/direct-message/${channelId}/${pId}`
276
+ : `/o/${orgName}/direct-message/${channelId}`
277
+ : pId
278
+ ? `/inbox/${channelId}/${pId}`
279
+ : `/inbox/${channelId}`;
280
+ const basePath = pathPrefix ? `${pathPrefix}${mainPath}` : mainPath;
281
+
282
+ const searchParams = new URLSearchParams();
283
+ // if (channelRole) searchParams.set('channelRole', channelRole);
284
+ // if (orgName) searchParams.set('orgName', orgName);
285
+
286
+ const newPath = searchParams.toString() ? `${basePath}?${searchParams.toString()}` : basePath;
287
+ navigate(newPath, { replace: true });
288
+
289
+ // Optimistically update Apollo cache for immediate UI feedback
290
+ try {
291
+ apolloClient.writeQuery({
292
+ query: MessagesDocument,
293
+ variables: {
294
+ channelId: channelId.toString(),
295
+ parentId: null,
296
+ limit: MESSAGES_PER_PAGE,
297
+ },
298
+ data: {
299
+ messages: {
300
+ __typename: 'Messages',
301
+ data: [],
302
+ totalCount: 0,
303
+ messagesRefId: channelId,
304
+ },
305
+ },
306
+ });
307
+ } catch (error) {
308
+ // Cache write might fail if query hasn't been executed yet, that's OK
309
+ console.debug('Cache write failed (expected on first load):', error);
310
+ }
311
+ },
312
+ [navigate, apolloClient, orgName, channelRole, pathPrefix],
313
+ );
314
+
315
+ const detailSidebarOptions = useMemo(
316
+ () => ({
317
+ isMobileView,
318
+ isSmallTabletView,
319
+ isTabletView,
320
+ isDesktopView,
321
+ isLargeDesktopView,
322
+ isSmallScreen,
323
+ setMobilePreviewCTAText: (v: string | ReactNode) =>
324
+ localDispatch({ payload: { mobilePreviewCTAText: v }, type: 'update' }),
325
+ setMobilePreviewText: (v: string | ReactNode) =>
326
+ localDispatch({ payload: { mobilePreviewText: v }, type: 'update' }),
327
+ setMobilePreviewVisibility: (v: boolean) =>
328
+ localDispatch({ payload: { mobilePreviewVisibility: v }, type: 'update' }),
329
+ }),
330
+ [isMobileView, isSmallTabletView, isTabletView, isDesktopView, isLargeDesktopView, isSmallScreen],
331
+ );
332
+
333
+ return (
334
+ <div
335
+ className="border-t border-gray-300 flex overflow-hidden"
336
+ style={{
337
+ height: `${windowHeight}px`,
338
+ maxHeight: '100vh',
339
+ }}
340
+ >
341
+ {/* Left Sidebar - Responsive Design */}
342
+ <div
343
+ className={`
344
+ flex-shrink-0 bg-gray-50 border-r border-gray-300 overflow-hidden transition-all duration-300 ease-in-out
345
+ ${isMobileView && pathChannelId ? 'hidden' : ''}
346
+ `}
347
+ style={{
348
+ width:
349
+ isMobileView && !pathChannelId
350
+ ? '100%'
351
+ : isMobileView && pathChannelId
352
+ ? '0px'
353
+ : isSmallTabletView
354
+ ? `${Math.min(288, windowWidth * 0.35)}px` // w-72 or 35% of window
355
+ : isTabletView
356
+ ? `${Math.min(320, windowWidth * 0.3)}px` // w-80 or 30% of window
357
+ : isLargeDesktopView
358
+ ? `${Math.min(384, windowWidth * 0.25)}px` // w-96 or 25% of window
359
+ : `${Math.min(320, windowWidth * 0.28)}px`, // w-80 or 28% of window
360
+ height: `${windowHeight}px`,
361
+ maxHeight: '100vh',
362
+ }}
363
+ >
364
+ <LeftSidebar
365
+ currentUser={currentUser}
366
+ userChannels={stableChannels}
367
+ userChannelsLoading={userChannelsLoading}
368
+ users={users}
369
+ handleSelectChannel={handleSelectChannel}
370
+ selectedChannelId={pathChannelId}
371
+ channelToTop={0}
372
+ getChannelsRefetch={getChannelsRefetch}
373
+ role={channelRole}
374
+ messagesQuery={data?.[1]}
375
+ windowHeight={windowHeight}
376
+ windowWidth={windowWidth}
377
+ />
378
+ </div>
379
+
380
+ {/* Main Content Area - Responsive */}
381
+ <div
382
+ className={`
383
+ flex-1 min-w-0 flex flex-col overflow-hidden transition-all duration-300 ease-in-out
384
+ ${isMobileView && !pathChannelId ? 'hidden' : 'flex'}
385
+ `}
386
+ style={{
387
+ minWidth: isSmallScreen ? '300px' : isDesktopView ? '500px' : '400px',
388
+ width: 'auto',
389
+ height: `${windowHeight}px`,
390
+ maxHeight: '100vh',
391
+ }}
392
+ >
393
+ {pathChannelId ? (
394
+ <ContentComponent
395
+ channelId={pathChannelId}
396
+ postId={pathPostId}
397
+ channelRole={channelRole}
398
+ pathPrefix={props.pathPrefix}
399
+ isMobileView={isMobileView}
400
+ isSmallTabletView={isSmallTabletView}
401
+ isTabletView={isTabletView}
402
+ isDesktopView={isDesktopView}
403
+ isLargeDesktopView={isLargeDesktopView}
404
+ isSmallScreen={isSmallScreen}
405
+ windowWidth={windowWidth}
406
+ windowHeight={windowHeight}
407
+ mobilePreviewState={mobilePreviewState}
408
+ detailSidebarOptions={detailSidebarOptions}
409
+ isBottomDrawerOpen={isBottomDrawerOpen}
410
+ setBottomDrawer={setBottomDrawer}
411
+ channelName={channelName}
412
+ loaderdata={data}
413
+ />
414
+ ) : (
415
+ <EmptyState />
416
+ )}
417
+ </div>
418
+
419
+ {/* Right Sidebar - Desktop Only */}
420
+ {pathChannelId && data?.[1] && isDesktopView && (
421
+ // <div
422
+ // className="border-l border-gray-200 bg-white flex-shrink-0 overflow-hidden"
423
+ // style={{
424
+ // width: isLargeDesktopView
425
+ // ? `${Math.min(384, windowWidth * 0.25)}px` // w-96 or 25% of window
426
+ // : `${Math.min(320, windowWidth * 0.22)}px`, // w-80 or 22% of window
427
+ // height: `${windowHeight}px`,
428
+ // maxHeight: '100vh'
429
+ // }}
430
+ // >
431
+ <RightSidebarWrapper
432
+ MessagesLoaderQuery={data?.[1]}
433
+ selectedPost={null}
434
+ detailSidebarOptions={detailSidebarOptions}
435
+ />
436
+ // </div>
437
+ )}
438
+ </div>
439
+ );
440
+ };
441
+
442
+ const ContentComponent = React.memo((props: any) => {
443
+ const {
444
+ channelId,
445
+ channelRole,
446
+ pathPrefix,
447
+ postId,
448
+ isMobileView,
449
+ isSmallTabletView,
450
+ isTabletView,
451
+ isDesktopView,
452
+ isLargeDesktopView,
453
+ isSmallScreen,
454
+ windowWidth,
455
+ windowHeight,
456
+ mobilePreviewState,
457
+ detailSidebarOptions,
458
+ isBottomDrawerOpen,
459
+ setBottomDrawer,
460
+ channelName,
461
+ loaderdata,
462
+ } = props;
463
+
464
+ const ViewChannelDetailLoaderQuery = loaderdata?.[2];
465
+ const MessagesLoaderQuery = loaderdata?.[1];
466
+
467
+ const [selectedPost, setSelectedPost] = React.useState(null);
468
+ const { data: channelData, loading: channelLoading } = ViewChannelDetailLoaderQuery || {};
469
+
470
+ const onMessageClick = useCallback((msg: any) => {
471
+ setSelectedPost(msg);
472
+ }, []);
473
+
474
+ const channelsDetail = useMemo(() => {
475
+ return channelData?.viewChannelDetail || null;
476
+ }, [channelData]);
477
+
478
+ return (
479
+ <div className="flex overflow-hidden" style={{ height: `${windowHeight}px`, maxHeight: '100vh' }}>
480
+ {/* Main Chat Content */}
481
+ <div className="flex-1 flex flex-col min-w-0 overflow-hidden">
482
+ {/* Channel Header */}
483
+ {channelId && (
484
+ <div
485
+ className={`border-b border-gray-200 bg-white flex-shrink-0 z-10 ${
486
+ isSmallScreen ? 'px-3 py-3' : 'px-4 sm:px-6 py-4'
487
+ }`}
488
+ >
489
+ <div className="flex items-center justify-between">
490
+ <div className="flex items-center space-x-2 min-w-0 flex-1">
491
+ {/* Mobile/Small Screen Back Button */}
492
+ {(isMobileView || isSmallTabletView) && (
493
+ <button
494
+ className="p-2 hover:bg-gray-100 rounded-full transition-colors flex-shrink-0"
495
+ onClick={() => window.history.back()}
496
+ aria-label="Go back"
497
+ >
498
+ <svg
499
+ className="w-5 h-5 text-gray-600"
500
+ fill="none"
501
+ stroke="currentColor"
502
+ viewBox="0 0 24 24"
503
+ >
504
+ <path
505
+ strokeLinecap="round"
506
+ strokeLinejoin="round"
507
+ strokeWidth={2}
508
+ d="M15 19l-7-7 7-7"
509
+ />
510
+ </svg>
511
+ </button>
512
+ )}
513
+ <h2
514
+ className={`font-semibold text-gray-800 truncate ${
515
+ isSmallScreen ? 'text-base' : 'text-lg'
516
+ }`}
517
+ >
518
+ {channelName}
519
+ </h2>
520
+ </div>
521
+ {(isMobileView || isSmallTabletView) && mobilePreviewState?.mobilePreviewVisibility && (
522
+ <button
523
+ className="text-sm px-3 py-1 bg-teal-500 hover:bg-teal-600 text-white rounded-md transition-colors flex-shrink-0"
524
+ onClick={() => setBottomDrawer(true)}
525
+ >
526
+ {mobilePreviewState?.mobilePreviewCTAText}
527
+ </button>
528
+ )}
529
+ </div>
530
+ </div>
531
+ )}
532
+
533
+ {/* Mobile Preview */}
534
+ {(isMobileView || isSmallTabletView) && channelId && mobilePreviewState?.mobilePreviewVisibility && (
535
+ <div className={`mt-4 ${isSmallScreen ? 'mx-3' : 'mx-4'}`}>
536
+ <div className="mb-2">
537
+ <div className="w-full flex justify-between items-center gap-2 mb-[5px]">
538
+ <span className="truncate flex-1 text-sm">{mobilePreviewState?.mobilePreviewText}</span>
539
+ <button
540
+ className="text-sm px-3 py-2 bg-teal-500 hover:bg-teal-600 text-white rounded-md transition-colors flex-shrink-0"
541
+ onClick={() => setBottomDrawer(true)}
542
+ >
543
+ {mobilePreviewState?.mobilePreviewCTAText}
544
+ </button>
545
+ </div>
546
+ </div>
547
+ <hr className="border-gray-200" />
548
+ </div>
549
+ )}
550
+
551
+ {/* Content based on postId */}
552
+ <div className="flex-1 flex flex-col min-h-0 overflow-hidden">
553
+ {channelId && (
554
+ <>
555
+ {postId ? (
556
+ postId === '1' ? (
557
+ <ThreadsInbox
558
+ channelId={channelId}
559
+ role={channelRole}
560
+ pathPrefix={pathPrefix}
561
+ setChannelId={() => {}}
562
+ setPostId={() => {}}
563
+ setGoBack={() => {}}
564
+ />
565
+ ) : (
566
+ <ThreadMessagesInbox
567
+ channelId={channelId}
568
+ postId={postId}
569
+ role={channelRole}
570
+ goBack={false}
571
+ pathPrefix={pathPrefix}
572
+ setPostId={() => {}}
573
+ setChannelId={() => {}}
574
+ onMessageClick={onMessageClick}
575
+ />
576
+ )
577
+ ) : (
578
+ <MessagesComponent
579
+ channelId={channelId}
580
+ MessagesLoaderQuery={MessagesLoaderQuery}
581
+ channelsDetail={channelsDetail}
582
+ channelLoading={channelLoading}
583
+ onMessageClick={onMessageClick}
584
+ isSmallScreen={isSmallScreen}
585
+ isDesktopView={isDesktopView}
586
+ windowHeight={windowHeight}
587
+ windowWidth={windowWidth}
588
+ />
589
+ )}
590
+ </>
591
+ )}
592
+ </div>
593
+ </div>
594
+
595
+ {/* Mobile/Small Screen Drawer */}
596
+ {(isMobileView || isSmallTabletView) && (
597
+ <Drawer
598
+ isOpen={isBottomDrawerOpen}
599
+ onClose={() => setBottomDrawer(false)}
600
+ title={mobilePreviewState.mobilePreviewText as string}
601
+ >
602
+ <RightSidebarWrapper
603
+ MessagesLoaderQuery={MessagesLoaderQuery}
604
+ selectedPost={selectedPost}
605
+ detailSidebarOptions={detailSidebarOptions}
606
+ />
607
+ </Drawer>
608
+ )}
609
+ </div>
610
+ );
611
+ });
612
+
613
+ const RightSidebarWrapper = React.memo(({ MessagesLoaderQuery, selectedPost, detailSidebarOptions }: any) => {
614
+ const { data } = MessagesLoaderQuery || {};
615
+
616
+ const sortedMessages = useMemo(() => {
617
+ const messages = data?.messages?.data || [];
618
+ return orderBy(uniqBy(messages, 'id'), ['createdAt'], ['asc']);
619
+ }, [data?.messages?.data]);
620
+
621
+ if (!sortedMessages.length) return null;
622
+
623
+ return (
624
+ <div className="h-full flex flex-col overflow-hidden">
625
+ <RightSidebar
626
+ channelMessages={sortedMessages}
627
+ visibility="visible"
628
+ selectedPost={selectedPost}
629
+ {...detailSidebarOptions}
630
+ />
631
+ </div>
632
+ );
633
+ });
634
+
635
+ const MessagesComponent = React.memo((props: any) => {
636
+ const {
637
+ channelId,
638
+ MessagesLoaderQuery,
639
+ channelsDetail,
640
+ channelLoading,
641
+ onMessageClick,
642
+ isSmallScreen,
643
+ isDesktopView,
644
+ windowHeight = 768,
645
+ windowWidth = 1024,
646
+ } = props;
647
+
648
+ const messageRootListRef = useRef(null);
649
+ const messageListRef = useRef(null);
650
+ const apolloClient = useApolloClient();
651
+ const [isLoadingOlder, setIsLoadingOlder] = React.useState(false);
652
+ const isLoadingOlderRef = useRef(false);
653
+ const scrollTimeoutRef = useRef(null);
654
+
655
+ const auth = useSelector(userSelector);
656
+ const { startUpload } = useUploadFiles();
657
+ const [sendMsg] = useSendMessagesMutation();
658
+
659
+ const { data, loading: messageLoading, fetchMore: fetchMoreMessages, subscribeToMore } = MessagesLoaderQuery || {};
660
+
661
+ // Get messages directly from Apollo cache
662
+ const messages = useMemo(() => {
663
+ const messagesData = data?.messages?.data || [];
664
+ return orderBy(uniqBy(messagesData, 'id'), ['createdAt'], ['asc']);
665
+ }, [data?.messages?.data]);
666
+
667
+ const totalCount = data?.messages?.totalCount || 0;
668
+
669
+ const scrollToBottom = useCallback(() => {
670
+ if (messageRootListRef?.current) {
671
+ messageRootListRef.current.scrollTop = messageRootListRef.current.scrollHeight;
672
+ }
673
+ }, []);
674
+
675
+ // Auto-scroll on new messages (but not when loading older messages)
676
+ useEffect(() => {
677
+ if (!isLoadingOlderRef.current) {
678
+ const timer = setTimeout(() => scrollToBottom(), 100);
679
+ return () => clearTimeout(timer);
680
+ }
681
+ }, [messages.length, scrollToBottom]);
682
+
683
+ const onFetchOld = useCallback(
684
+ async (skip: number) => {
685
+ if (channelId && fetchMoreMessages && !isLoadingOlder) {
686
+ try {
687
+ setIsLoadingOlder(true);
688
+ isLoadingOlderRef.current = true;
689
+ // Capture current scroll height before fetching
690
+ const oldScrollHeight = messageRootListRef?.current?.scrollHeight || 0;
691
+
692
+ await fetchMoreMessages({
693
+ variables: {
694
+ channelId: channelId.toString(),
695
+ parentId: null,
696
+ skip,
697
+ },
698
+ updateQuery: (prev, { fetchMoreResult }) => {
699
+ if (!fetchMoreResult) return prev;
700
+
701
+ const newMessages = fetchMoreResult.messages.data;
702
+ const existingMessages = prev.messages?.data || [];
703
+
704
+ return {
705
+ ...prev,
706
+ messages: {
707
+ ...fetchMoreResult.messages,
708
+ data: uniqBy([...newMessages, ...existingMessages], 'id'),
709
+ },
710
+ };
711
+ },
712
+ });
713
+
714
+ // Maintain scroll position after loading older messages
715
+ setTimeout(() => {
716
+ if (messageRootListRef?.current) {
717
+ const newScrollHeight = messageRootListRef.current.scrollHeight;
718
+ const scrollDiff = newScrollHeight - oldScrollHeight;
719
+ // For normal flex layout, maintain position by adjusting scroll offset
720
+ messageRootListRef.current.scrollTop = scrollDiff;
721
+ }
722
+ // Reset the loading flag after position is maintained
723
+ setTimeout(() => {
724
+ isLoadingOlderRef.current = false;
725
+ }, 50);
726
+ }, 100);
727
+ } catch (error) {
728
+ console.error('Error fetching older messages:', error);
729
+ isLoadingOlderRef.current = false;
730
+ } finally {
731
+ setIsLoadingOlder(false);
732
+ }
733
+ }
734
+ },
735
+ [channelId, fetchMoreMessages, isLoadingOlder],
736
+ );
737
+
738
+ // Scroll to bottom when channel changes
739
+ useEffect(() => {
740
+ if (channelId && messages.length > 0) {
741
+ isLoadingOlderRef.current = false; // Reset flag on channel change
742
+ const timer = setTimeout(() => scrollToBottom(), 200);
743
+ return () => clearTimeout(timer);
744
+ }
745
+ }, [channelId, scrollToBottom]);
746
+
747
+ // Alternative scroll detection for Firefox
748
+ useEffect(() => {
749
+ const element = messageRootListRef.current;
750
+ if (!element) return;
751
+
752
+ // Firefox-specific scroll detection using passive listeners
753
+ const handleScrollEnd = () => {
754
+ if (!isLoadingOlder && element) {
755
+ const { scrollTop } = element;
756
+ const isAtTop = Math.round(scrollTop) <= 30;
757
+ const hasMoreMessages = totalCount > messages.length;
758
+
759
+ if (isAtTop && hasMoreMessages) {
760
+ console.log('ScrollEnd triggered load more (Firefox):', {
761
+ scrollTop: Math.round(scrollTop),
762
+ totalCount,
763
+ messagesLength: messages.length,
764
+ });
765
+ onFetchOld(messages.length);
766
+ }
767
+ }
768
+ };
769
+
770
+ // Use scrollend event if available (modern Firefox/Chrome)
771
+ if ('onscrollend' in element) {
772
+ element.addEventListener('scrollend', handleScrollEnd, { passive: true });
773
+ return () => {
774
+ element.removeEventListener('scrollend', handleScrollEnd);
775
+ };
776
+ }
777
+ }, [totalCount, messages.length, onFetchOld, isLoadingOlder]);
778
+
779
+ // Cleanup scroll timeout on unmount
780
+ useEffect(() => {
781
+ return () => {
782
+ if (scrollTimeoutRef.current) {
783
+ clearTimeout(scrollTimeoutRef.current);
784
+ }
785
+ };
786
+ }, []);
787
+
788
+ const onMessagesScroll = useCallback(
789
+ async (e: any) => {
790
+ // Throttle scroll events for better performance, especially in Firefox
791
+ if (scrollTimeoutRef.current) {
792
+ clearTimeout(scrollTimeoutRef.current);
793
+ }
794
+
795
+ scrollTimeoutRef.current = setTimeout(async () => {
796
+ if (messageRootListRef.current && !isLoadingOlder) {
797
+ const element = messageRootListRef.current;
798
+ const { clientHeight, scrollHeight, scrollTop } = element;
799
+
800
+ // Firefox-compatible scroll detection
801
+ // Use Math.ceil to handle Firefox's fractional scrollTop values
802
+ const isAtTop = Math.ceil(scrollTop) <= 25;
803
+ const hasMoreMessages = totalCount > messages.length;
804
+
805
+ // Additional Firefox-specific check
806
+ const isFirefox = navigator.userAgent.includes('Firefox');
807
+ const firefoxAdjustedTop = isFirefox ? Math.round(scrollTop) <= 30 : isAtTop;
808
+
809
+ if ((isAtTop || firefoxAdjustedTop) && hasMoreMessages) {
810
+ console.log('Triggering load more:', {
811
+ scrollTop: Math.ceil(scrollTop),
812
+ originalScrollTop: scrollTop,
813
+ totalCount,
814
+ messagesLength: messages.length,
815
+ scrollHeight,
816
+ clientHeight,
817
+ browser: isFirefox ? 'Firefox' : 'Other',
818
+ isAtTop,
819
+ firefoxAdjustedTop,
820
+ });
821
+ await onFetchOld(messages.length);
822
+ }
823
+ }
824
+ }, 100);
825
+ },
826
+ [totalCount, messages.length, onFetchOld, isLoadingOlder],
827
+ );
828
+
829
+ // Optimistic message sending with Apollo cache updates
830
+ const handleSend = useCallback(
831
+ async (message: string, files: any[] = []) => {
832
+ // Allow sending if there's either a message or files
833
+ if ((!message || !message.trim()) && (!files || files.length === 0)) return;
834
+ if (!channelId) return;
835
+
836
+ try {
837
+ const postId = objectId();
838
+ const currentDate = new Date();
839
+
840
+ const createOptimisticMessage = (files?: any[]) => ({
841
+ __typename: 'Post' as const,
842
+ id: postId,
843
+ message,
844
+ createdAt: currentDate.toISOString(),
845
+ updatedAt: currentDate.toISOString(),
846
+ author: {
847
+ __typename: 'UserAccount' as const,
848
+ id: auth?.id,
849
+ givenName: auth?.profile?.given_name || '',
850
+ familyName: auth?.profile?.family_name || '',
851
+ email: auth?.profile?.email || '',
852
+ username: auth?.profile?.nickname || '',
853
+ fullName: auth?.profile?.name || '',
854
+ picture: auth?.profile?.picture || '',
855
+ alias: [auth?.authUserId ?? ''],
856
+ tokens: [],
857
+ },
858
+ isDelivered: false, // Will be true once confirmed by server
859
+ isRead: false,
860
+ type: 'TEXT' as PostTypeEnum,
861
+ parentId: null,
862
+ fromServer: false,
863
+ channel: {
864
+ __typename: 'Channel' as const,
865
+ id: channelId,
866
+ },
867
+ propsConfiguration: {
868
+ __typename: 'MachineConfiguration' as const,
869
+ id: null,
870
+ resource: '' as any,
871
+ contents: null,
872
+ keys: null,
873
+ target: null,
874
+ overrides: null,
875
+ },
876
+ props: {},
877
+ files: {
878
+ __typename: 'FilesInfo' as const,
879
+ data: files || [],
880
+ totalCount: files?.length || 0,
881
+ },
882
+ replies: {
883
+ __typename: 'Messages' as const,
884
+ data: [],
885
+ totalCount: 0,
886
+ },
887
+ });
888
+
889
+ const optimisticMessage = createOptimisticMessage(files);
890
+
891
+ if (files?.length > 0) {
892
+ const uploadResponse = await startUpload({
893
+ file: files,
894
+ saveUploadedFile: { variables: { postId } },
895
+ createUploadLink: { variables: { postId } },
896
+ });
897
+
898
+ const uploadedFiles = uploadResponse.data as unknown as IFileInfo[];
899
+ if (uploadedFiles) {
900
+ const fileIds = uploadedFiles.map((f: any) => f.id);
901
+ await sendMsg({
902
+ variables: { postId, channelId, content: message, files: fileIds },
903
+ optimisticResponse: {
904
+ __typename: 'Mutation',
905
+ sendMessage: createOptimisticMessage(uploadedFiles),
906
+ },
907
+ update: (cache, { data: mutationData }) => {
908
+ if (!mutationData?.sendMessage) return;
909
+
910
+ // Update messages cache optimistically
911
+ const messagesQuery = {
912
+ query: MessagesDocument,
913
+ variables: {
914
+ channelId: channelId.toString(),
915
+ parentId: null,
916
+ limit: MESSAGES_PER_PAGE,
917
+ },
918
+ };
919
+
920
+ try {
921
+ const existingData = cache.readQuery(messagesQuery) as any;
922
+ if (existingData?.messages) {
923
+ cache.writeQuery({
924
+ ...messagesQuery,
925
+ data: {
926
+ messages: {
927
+ ...existingData.messages,
928
+ data: [
929
+ ...(existingData.messages.data || []),
930
+ mutationData.sendMessage,
931
+ ],
932
+ totalCount: (existingData.messages.totalCount || 0) + 1,
933
+ },
934
+ },
935
+ });
936
+ }
937
+ } catch (error) {
938
+ console.debug('Cache update failed (expected on first message):', error);
939
+ }
940
+ },
941
+ });
942
+ }
943
+ } else {
944
+ await sendMsg({
945
+ variables: { channelId, content: message },
946
+ optimisticResponse: {
947
+ __typename: 'Mutation',
948
+ sendMessage: optimisticMessage,
949
+ },
950
+ update: (cache, { data: mutationData }) => {
951
+ if (!mutationData?.sendMessage) return;
952
+
953
+ // Update messages cache optimistically
954
+ const messagesQuery = {
955
+ query: MessagesDocument,
956
+ variables: {
957
+ channelId: channelId.toString(),
958
+ parentId: null,
959
+ limit: MESSAGES_PER_PAGE,
960
+ },
961
+ };
962
+
963
+ try {
964
+ const existingData = cache.readQuery(messagesQuery) as any;
965
+ if (existingData?.messages) {
966
+ cache.writeQuery({
967
+ ...messagesQuery,
968
+ data: {
969
+ messages: {
970
+ ...existingData.messages,
971
+ data: [...(existingData.messages.data || []), mutationData.sendMessage],
972
+ totalCount: (existingData.messages.totalCount || 0) + 1,
973
+ },
974
+ },
975
+ });
976
+ }
977
+ } catch (error) {
978
+ console.debug('Cache update failed (expected on first message):', error);
979
+ }
980
+ },
981
+ });
982
+ }
983
+ } catch (error) {
984
+ console.error('Error sending message:', error);
985
+ }
986
+ },
987
+ [channelId, auth, startUpload, sendMsg],
988
+ );
989
+
990
+ // Show loading spinner for initial load
991
+ if ((messageLoading || channelLoading) && messages.length === 0) {
992
+ return (
993
+ <div className="flex-1 flex justify-center items-center">
994
+ <Spinner className="w-12 h-12" />
995
+ </div>
996
+ );
997
+ }
998
+
999
+ return (
1000
+ <>
1001
+ <div
1002
+ ref={messageRootListRef}
1003
+ className={`overflow-y-scroll bg-gray-50 ${
1004
+ isSmallScreen ? 'p-2 px-3' : isDesktopView ? 'p-6 px-8' : 'p-4 px-6'
1005
+ }`}
1006
+ onScroll={onMessagesScroll}
1007
+ style={{
1008
+ height: `${windowHeight - 140}px`, // Subtract header + input height
1009
+ maxHeight: '100vh',
1010
+ scrollbarWidth: 'thin',
1011
+ scrollbarColor: '#cbd5e0 #f7fafc',
1012
+ overflowY: 'scroll',
1013
+ WebkitOverflowScrolling: 'touch',
1014
+ }}
1015
+ >
1016
+ <div className="min-h-full">
1017
+ {messages.length > 0 ? (
1018
+ <>
1019
+ {/* Loading indicator for older messages at the top */}
1020
+ {isLoadingOlder && (
1021
+ <div className="flex justify-center py-4">
1022
+ <div className="flex items-center space-x-2 text-gray-500">
1023
+ <Spinner className="w-4 h-4" />
1024
+ <span className="text-sm">Loading older messages...</span>
1025
+ </div>
1026
+ </div>
1027
+ )}
1028
+ <Messages
1029
+ innerRef={messageListRef}
1030
+ channelId={channelId}
1031
+ currentUser={auth}
1032
+ channelMessages={messages}
1033
+ totalCount={totalCount}
1034
+ onMessageClick={onMessageClick}
1035
+ isDesktopView={isDesktopView || false}
1036
+ isSmallScreen={isSmallScreen || false}
1037
+ />
1038
+ <SubscriptionHandler
1039
+ subscribeToMore={subscribeToMore}
1040
+ document={CHAT_MESSAGE_ADDED}
1041
+ variables={{ channelId: channelId.toString() }}
1042
+ enabled={!!channelId && !!subscribeToMore}
1043
+ updateQuery={(prev: any, { subscriptionData }: any) => {
1044
+ console.log('Subscription updateQuery called:', { prev, subscriptionData });
1045
+ if (!subscriptionData.data) {
1046
+ console.log('No subscription data, returning prev');
1047
+ return prev;
1048
+ }
1049
+ const newMessage = subscriptionData.data.chatMessageAdded;
1050
+ console.log('New message received via subscription:', newMessage);
1051
+
1052
+ return {
1053
+ ...prev,
1054
+ messages: {
1055
+ ...prev?.messages,
1056
+ data: uniqBy([...(prev?.messages?.data || []), newMessage], 'id'),
1057
+ totalCount: (prev?.messages?.totalCount || 0) + 1,
1058
+ },
1059
+ };
1060
+ }}
1061
+ onError={(error) => {
1062
+ console.error('Subscription error:', error);
1063
+ }}
1064
+ />
1065
+ </>
1066
+ ) : (
1067
+ <div className="flex items-center justify-center text-gray-500 min-h-96">
1068
+ <div className="text-center max-w-sm mx-auto px-4">
1069
+ <div className="text-6xl mb-4 opacity-50">💬</div>
1070
+ <h3 className="text-lg font-semibold text-gray-600 mb-2">No messages yet</h3>
1071
+ <p className="text-sm text-gray-500">
1072
+ Start the conversation by sending a message below!
1073
+ </p>
1074
+ </div>
1075
+ </div>
1076
+ )}
1077
+ </div>
1078
+ </div>
1079
+ <div className="flex-shrink-0 border-t border-gray-200 bg-white">
1080
+ <MessageInput channelId={channelId} handleSend={handleSend} placeholder="Message" />
1081
+ </div>
1082
+ </>
1083
+ );
1084
+ });
1085
+
1086
+ // Display names for debugging
1087
+ Inbox.displayName = 'Inbox';
1088
+ ContentComponent.displayName = 'ContentComponent';
1089
+ MessagesComponent.displayName = 'MessagesComponent';
1090
+ RightSidebarWrapper.displayName = 'RightSidebarWrapper';
1091
+
1092
+ export default React.memo(Inbox);