@droppii-org/chat-mobile 0.2.7 → 0.2.8

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 (323) hide show
  1. package/lib/module/components/AttachmentPreview.js +250 -0
  2. package/lib/module/components/AttachmentPreview.js.map +1 -0
  3. package/lib/module/components/MediaViewer/index.js +2 -0
  4. package/lib/module/components/MediaViewer/index.js.map +1 -0
  5. package/lib/module/components/MediaViewerModal.js +57 -0
  6. package/lib/module/components/MediaViewerModal.js.map +1 -0
  7. package/lib/module/components/MergedImageGrid.js +173 -0
  8. package/lib/module/components/MergedImageGrid.js.map +1 -0
  9. package/lib/module/components/ThreadCard/thread-card.utils.js +80 -4
  10. package/lib/module/components/ThreadCard/thread-card.utils.js.map +1 -1
  11. package/lib/module/components/messages/fileMessage/index.js +26 -0
  12. package/lib/module/components/messages/fileMessage/index.js.map +1 -0
  13. package/lib/module/components/messages/imageMessage/index.js +118 -0
  14. package/lib/module/components/messages/imageMessage/index.js.map +1 -0
  15. package/lib/module/components/messages/linkMessage/index.js +122 -0
  16. package/lib/module/components/messages/linkMessage/index.js.map +1 -0
  17. package/lib/module/components/messages/mergedMessage/index.js +37 -0
  18. package/lib/module/components/messages/mergedMessage/index.js.map +1 -0
  19. package/lib/module/components/messages/styles.js +41 -0
  20. package/lib/module/components/messages/styles.js.map +1 -0
  21. package/lib/module/components/messages/textMessage/index.js +38 -0
  22. package/lib/module/components/messages/textMessage/index.js.map +1 -0
  23. package/lib/module/components/messages/types.js +14 -0
  24. package/lib/module/components/messages/types.js.map +1 -0
  25. package/lib/module/components/messages/videoMessage/index.js +110 -0
  26. package/lib/module/components/messages/videoMessage/index.js.map +1 -0
  27. package/lib/module/config/api-endpoints.js +66 -0
  28. package/lib/module/config/api-endpoints.js.map +1 -0
  29. package/lib/module/config/attachment-priority.js +81 -0
  30. package/lib/module/config/attachment-priority.js.map +1 -0
  31. package/lib/module/config/configuration.js +50 -0
  32. package/lib/module/config/configuration.js.map +1 -0
  33. package/lib/module/config/index.js +22 -0
  34. package/lib/module/config/index.js.map +1 -0
  35. package/lib/module/core/index.js +19 -1
  36. package/lib/module/core/index.js.map +1 -1
  37. package/lib/module/core/useChatListener.js +0 -14
  38. package/lib/module/core/useChatListener.js.map +1 -1
  39. package/lib/module/hooks/message/useSendMessage.js +10 -5
  40. package/lib/module/hooks/message/useSendMessage.js.map +1 -1
  41. package/lib/module/hooks/useChatMessages.js +0 -3
  42. package/lib/module/hooks/useChatMessages.js.map +1 -1
  43. package/lib/module/hooks/useImageAttachment.js +263 -0
  44. package/lib/module/hooks/useImageAttachment.js.map +1 -0
  45. package/lib/module/hooks/useMediaViewer.js +24 -0
  46. package/lib/module/hooks/useMediaViewer.js.map +1 -0
  47. package/lib/module/hooks/useSendAttachment.js +182 -0
  48. package/lib/module/hooks/useSendAttachment.js.map +1 -0
  49. package/lib/module/hooks/useVideoAttachment.js +251 -0
  50. package/lib/module/hooks/useVideoAttachment.js.map +1 -0
  51. package/lib/module/index.js +13 -1
  52. package/lib/module/index.js.map +1 -1
  53. package/lib/module/screens/MediaView/VideoPlayer.js +177 -0
  54. package/lib/module/screens/MediaView/VideoPlayer.js.map +1 -0
  55. package/lib/module/screens/MediaView/index.js +264 -0
  56. package/lib/module/screens/MediaView/index.js.map +1 -0
  57. package/lib/module/screens/chat-detail/ChatComposer.js +190 -196
  58. package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
  59. package/lib/module/screens/chat-detail/ChatDetail.js +94 -63
  60. package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
  61. package/lib/module/screens/chat-detail/ChatListLegend.js +5 -13
  62. package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
  63. package/lib/module/screens/chat-detail/components/ChatInputActions.js +51 -0
  64. package/lib/module/screens/chat-detail/components/ChatInputActions.js.map +1 -0
  65. package/lib/module/screens/chat-detail/components/ChatMessageInput.js +93 -0
  66. package/lib/module/screens/chat-detail/components/ChatMessageInput.js.map +1 -0
  67. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js +221 -0
  68. package/lib/module/screens/chat-detail/hooks/useAttachmentSendHandler.js.map +1 -0
  69. package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js +114 -0
  70. package/lib/module/screens/chat-detail/hooks/useChatComposerAnimation.js.map +1 -0
  71. package/lib/module/screens/chat-detail/hooks/useChatComposerState.js +26 -0
  72. package/lib/module/screens/chat-detail/hooks/useChatComposerState.js.map +1 -0
  73. package/lib/module/screens/chat-detail/index.js +0 -1
  74. package/lib/module/screens/chat-detail/index.js.map +1 -1
  75. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +33 -13
  76. package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
  77. package/lib/module/screens/chat-detail/legend/messageTypes.js +15 -0
  78. package/lib/module/screens/chat-detail/legend/messageTypes.js.map +1 -0
  79. package/lib/module/screens/inbox/MessagesTab.js.map +1 -1
  80. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js +61 -0
  81. package/lib/module/services/attachmentHandlers/fileAttachmentHandler.js.map +1 -0
  82. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js +54 -0
  83. package/lib/module/services/attachmentHandlers/imageAttachmentHandler.js.map +1 -0
  84. package/lib/module/services/attachmentHandlers/index.js +12 -0
  85. package/lib/module/services/attachmentHandlers/index.js.map +1 -0
  86. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js +85 -0
  87. package/lib/module/services/attachmentHandlers/videoAttachmentHandler.js.map +1 -0
  88. package/lib/module/services/attachmentOrchestrator.js +225 -0
  89. package/lib/module/services/attachmentOrchestrator.js.map +1 -0
  90. package/lib/module/services/auth.js +35 -0
  91. package/lib/module/services/auth.js.map +1 -0
  92. package/lib/module/services/endpoints.js +20 -1
  93. package/lib/module/services/endpoints.js.map +1 -1
  94. package/lib/module/services/imageUpload.js +126 -0
  95. package/lib/module/services/imageUpload.js.map +1 -0
  96. package/lib/module/translation/resources/i18n.js +22 -8
  97. package/lib/module/translation/resources/i18n.js.map +1 -1
  98. package/lib/module/types/attachment.js +2 -0
  99. package/lib/module/types/attachment.js.map +1 -0
  100. package/lib/module/types/attachmentHandler.js +20 -0
  101. package/lib/module/types/attachmentHandler.js.map +1 -0
  102. package/lib/module/types/imageUpload.js +2 -0
  103. package/lib/module/types/imageUpload.js.map +1 -0
  104. package/lib/module/types/message.js +1 -0
  105. package/lib/module/types/message.js.map +1 -1
  106. package/lib/module/utils/chatImageDimens.js +148 -0
  107. package/lib/module/utils/chatImageDimens.js.map +1 -0
  108. package/lib/module/utils/device.js +65 -0
  109. package/lib/module/utils/device.js.map +1 -0
  110. package/lib/module/utils/imageUrlOptimizer.js +41 -0
  111. package/lib/module/utils/imageUrlOptimizer.js.map +1 -0
  112. package/lib/module/utils/imageUtils.js +69 -0
  113. package/lib/module/utils/imageUtils.js.map +1 -0
  114. package/lib/module/utils/resolveMessageType.js +3 -0
  115. package/lib/module/utils/resolveMessageType.js.map +1 -1
  116. package/lib/module/utils/ui.js +17 -0
  117. package/lib/module/utils/ui.js.map +1 -0
  118. package/lib/module/utils/url.js +1 -1
  119. package/lib/module/utils/url.js.map +1 -1
  120. package/lib/module/utils/videoThumbnail.js +62 -0
  121. package/lib/module/utils/videoThumbnail.js.map +1 -0
  122. package/lib/typescript/src/components/AttachmentPreview.d.ts +28 -0
  123. package/lib/typescript/src/components/AttachmentPreview.d.ts.map +1 -0
  124. package/lib/typescript/src/components/MediaViewer/index.d.ts +1 -0
  125. package/lib/typescript/src/components/MediaViewer/index.d.ts.map +1 -0
  126. package/lib/typescript/src/components/MediaViewerModal.d.ts +10 -0
  127. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -0
  128. package/lib/typescript/src/components/MergedImageGrid.d.ts +16 -0
  129. package/lib/typescript/src/components/MergedImageGrid.d.ts.map +1 -0
  130. package/lib/typescript/src/components/ThreadCard/thread-card.utils.d.ts.map +1 -1
  131. package/lib/typescript/src/components/messages/fileMessage/index.d.ts +3 -0
  132. package/lib/typescript/src/components/messages/fileMessage/index.d.ts.map +1 -0
  133. package/lib/typescript/src/components/messages/imageMessage/index.d.ts +3 -0
  134. package/lib/typescript/src/components/messages/imageMessage/index.d.ts.map +1 -0
  135. package/lib/typescript/src/components/messages/linkMessage/index.d.ts +9 -0
  136. package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -0
  137. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts +3 -0
  138. package/lib/typescript/src/components/messages/mergedMessage/index.d.ts.map +1 -0
  139. package/lib/typescript/src/components/messages/styles.d.ts +36 -0
  140. package/lib/typescript/src/components/messages/styles.d.ts.map +1 -0
  141. package/lib/typescript/src/components/messages/textMessage/index.d.ts +9 -0
  142. package/lib/typescript/src/components/messages/textMessage/index.d.ts.map +1 -0
  143. package/lib/typescript/src/components/messages/types.d.ts +10 -0
  144. package/lib/typescript/src/components/messages/types.d.ts.map +1 -0
  145. package/lib/typescript/src/components/messages/videoMessage/index.d.ts +3 -0
  146. package/lib/typescript/src/components/messages/videoMessage/index.d.ts.map +1 -0
  147. package/lib/typescript/src/config/api-endpoints.d.ts +40 -0
  148. package/lib/typescript/src/config/api-endpoints.d.ts.map +1 -0
  149. package/lib/typescript/src/config/attachment-priority.d.ts +31 -0
  150. package/lib/typescript/src/config/attachment-priority.d.ts.map +1 -0
  151. package/lib/typescript/src/config/configuration.d.ts +30 -0
  152. package/lib/typescript/src/config/configuration.d.ts.map +1 -0
  153. package/lib/typescript/src/config/index.d.ts +15 -0
  154. package/lib/typescript/src/config/index.d.ts.map +1 -0
  155. package/lib/typescript/src/core/index.d.ts +13 -1
  156. package/lib/typescript/src/core/index.d.ts.map +1 -1
  157. package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
  158. package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
  159. package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
  160. package/lib/typescript/src/hooks/useImageAttachment.d.ts +20 -0
  161. package/lib/typescript/src/hooks/useImageAttachment.d.ts.map +1 -0
  162. package/lib/typescript/src/hooks/useMediaViewer.d.ts +13 -0
  163. package/lib/typescript/src/hooks/useMediaViewer.d.ts.map +1 -0
  164. package/lib/typescript/src/hooks/useSendAttachment.d.ts +59 -0
  165. package/lib/typescript/src/hooks/useSendAttachment.d.ts.map +1 -0
  166. package/lib/typescript/src/hooks/useVideoAttachment.d.ts +29 -0
  167. package/lib/typescript/src/hooks/useVideoAttachment.d.ts.map +1 -0
  168. package/lib/typescript/src/index.d.ts +14 -1
  169. package/lib/typescript/src/index.d.ts.map +1 -1
  170. package/lib/typescript/src/screens/MediaView/VideoPlayer.d.ts +12 -0
  171. package/lib/typescript/src/screens/MediaView/VideoPlayer.d.ts.map +1 -0
  172. package/lib/typescript/src/screens/MediaView/index.d.ts +11 -0
  173. package/lib/typescript/src/screens/MediaView/index.d.ts.map +1 -0
  174. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts +1 -1
  175. package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
  176. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts +1 -1
  177. package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
  178. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts +1 -1
  179. package/lib/typescript/src/screens/chat-detail/ChatListLegend.d.ts.map +1 -1
  180. package/lib/typescript/src/screens/chat-detail/components/ChatInputActions.d.ts +8 -0
  181. package/lib/typescript/src/screens/chat-detail/components/ChatInputActions.d.ts.map +1 -0
  182. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts +15 -0
  183. package/lib/typescript/src/screens/chat-detail/components/ChatMessageInput.d.ts.map +1 -0
  184. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts +23 -0
  185. package/lib/typescript/src/screens/chat-detail/hooks/useAttachmentSendHandler.d.ts.map +1 -0
  186. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts +15 -0
  187. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerAnimation.d.ts.map +1 -0
  188. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts +13 -0
  189. package/lib/typescript/src/screens/chat-detail/hooks/useChatComposerState.d.ts.map +1 -0
  190. package/lib/typescript/src/screens/chat-detail/index.d.ts +0 -2
  191. package/lib/typescript/src/screens/chat-detail/index.d.ts.map +1 -1
  192. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +3 -1
  193. package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
  194. package/lib/typescript/src/screens/chat-detail/legend/messageTypes.d.ts +13 -0
  195. package/lib/typescript/src/screens/chat-detail/legend/messageTypes.d.ts.map +1 -0
  196. package/lib/typescript/src/screens/chat-detail/types.d.ts +4 -1
  197. package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
  198. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts +22 -0
  199. package/lib/typescript/src/services/attachmentHandlers/fileAttachmentHandler.d.ts.map +1 -0
  200. package/lib/typescript/src/services/attachmentHandlers/imageAttachmentHandler.d.ts +3 -0
  201. package/lib/typescript/src/services/attachmentHandlers/imageAttachmentHandler.d.ts.map +1 -0
  202. package/lib/typescript/src/services/attachmentHandlers/index.d.ts +5 -0
  203. package/lib/typescript/src/services/attachmentHandlers/index.d.ts.map +1 -0
  204. package/lib/typescript/src/services/attachmentHandlers/videoAttachmentHandler.d.ts +31 -0
  205. package/lib/typescript/src/services/attachmentHandlers/videoAttachmentHandler.d.ts.map +1 -0
  206. package/lib/typescript/src/services/attachmentOrchestrator.d.ts +5 -0
  207. package/lib/typescript/src/services/attachmentOrchestrator.d.ts.map +1 -0
  208. package/lib/typescript/src/services/auth.d.ts +19 -0
  209. package/lib/typescript/src/services/auth.d.ts.map +1 -0
  210. package/lib/typescript/src/services/endpoints.d.ts +11 -1
  211. package/lib/typescript/src/services/endpoints.d.ts.map +1 -1
  212. package/lib/typescript/src/services/imageUpload.d.ts +7 -0
  213. package/lib/typescript/src/services/imageUpload.d.ts.map +1 -0
  214. package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
  215. package/lib/typescript/src/types/attachment.d.ts +72 -0
  216. package/lib/typescript/src/types/attachment.d.ts.map +1 -0
  217. package/lib/typescript/src/types/attachmentHandler.d.ts +13 -0
  218. package/lib/typescript/src/types/attachmentHandler.d.ts.map +1 -0
  219. package/lib/typescript/src/types/imageUpload.d.ts +26 -0
  220. package/lib/typescript/src/types/imageUpload.d.ts.map +1 -0
  221. package/lib/typescript/src/types/message.d.ts +1 -0
  222. package/lib/typescript/src/types/message.d.ts.map +1 -1
  223. package/lib/typescript/src/utils/chatImageDimens.d.ts +34 -0
  224. package/lib/typescript/src/utils/chatImageDimens.d.ts.map +1 -0
  225. package/lib/typescript/src/utils/device.d.ts +7 -0
  226. package/lib/typescript/src/utils/device.d.ts.map +1 -0
  227. package/lib/typescript/src/utils/imageUrlOptimizer.d.ts +12 -0
  228. package/lib/typescript/src/utils/imageUrlOptimizer.d.ts.map +1 -0
  229. package/lib/typescript/src/utils/imageUtils.d.ts +9 -0
  230. package/lib/typescript/src/utils/imageUtils.d.ts.map +1 -0
  231. package/lib/typescript/src/utils/resolveMessageType.d.ts.map +1 -1
  232. package/lib/typescript/src/utils/ui.d.ts +13 -0
  233. package/lib/typescript/src/utils/ui.d.ts.map +1 -0
  234. package/lib/typescript/src/utils/videoThumbnail.d.ts +16 -0
  235. package/lib/typescript/src/utils/videoThumbnail.d.ts.map +1 -0
  236. package/package.json +15 -3
  237. package/src/components/AttachmentPreview.tsx +304 -0
  238. package/src/components/MediaViewer/index.tsx +0 -0
  239. package/src/components/MediaViewerModal.tsx +70 -0
  240. package/src/components/MergedImageGrid.tsx +238 -0
  241. package/src/components/ThreadCard/thread-card.utils.ts +95 -4
  242. package/src/components/messages/fileMessage/index.tsx +30 -0
  243. package/src/components/messages/imageMessage/index.tsx +137 -0
  244. package/src/components/messages/linkMessage/index.tsx +162 -0
  245. package/src/components/messages/mergedMessage/index.tsx +45 -0
  246. package/src/components/messages/styles.ts +39 -0
  247. package/src/components/messages/textMessage/index.tsx +53 -0
  248. package/src/components/messages/types.ts +22 -0
  249. package/src/components/messages/videoMessage/index.tsx +120 -0
  250. package/src/config/api-endpoints.ts +72 -0
  251. package/src/config/attachment-priority.ts +93 -0
  252. package/src/config/configuration.ts +50 -0
  253. package/src/config/index.ts +19 -0
  254. package/src/core/index.ts +25 -1
  255. package/src/core/useChatListener.ts +0 -21
  256. package/src/hooks/message/useSendMessage.ts +12 -5
  257. package/src/hooks/useChatMessages.ts +0 -4
  258. package/src/hooks/useImageAttachment.ts +348 -0
  259. package/src/hooks/useMediaViewer.ts +32 -0
  260. package/src/hooks/useSendAttachment.ts +295 -0
  261. package/src/hooks/useVideoAttachment.ts +334 -0
  262. package/src/index.tsx +13 -1
  263. package/src/screens/MediaView/VideoPlayer.tsx +211 -0
  264. package/src/screens/MediaView/index.tsx +327 -0
  265. package/src/screens/chat-detail/ChatComposer.tsx +206 -271
  266. package/src/screens/chat-detail/ChatDetail.tsx +142 -89
  267. package/src/screens/chat-detail/ChatListLegend.tsx +9 -11
  268. package/src/screens/chat-detail/components/ChatInputActions.tsx +71 -0
  269. package/src/screens/chat-detail/components/ChatMessageInput.tsx +127 -0
  270. package/src/screens/chat-detail/hooks/useAttachmentSendHandler.ts +291 -0
  271. package/src/screens/chat-detail/hooks/useChatComposerAnimation.ts +184 -0
  272. package/src/screens/chat-detail/hooks/useChatComposerState.ts +40 -0
  273. package/src/screens/chat-detail/index.ts +0 -2
  274. package/src/screens/chat-detail/legend/LegendChatMessage.tsx +47 -24
  275. package/src/screens/chat-detail/legend/messageTypes.tsx +13 -0
  276. package/src/screens/chat-detail/types.ts +5 -1
  277. package/src/screens/inbox/MessagesTab.tsx +1 -1
  278. package/src/services/attachmentHandlers/fileAttachmentHandler.ts +78 -0
  279. package/src/services/attachmentHandlers/imageAttachmentHandler.ts +54 -0
  280. package/src/services/attachmentHandlers/index.ts +10 -0
  281. package/src/services/attachmentHandlers/videoAttachmentHandler.ts +114 -0
  282. package/src/services/attachmentOrchestrator.ts +300 -0
  283. package/src/services/auth.ts +34 -0
  284. package/src/services/endpoints.ts +24 -1
  285. package/src/services/imageUpload.ts +162 -0
  286. package/src/translation/resources/i18n.ts +22 -8
  287. package/src/types/attachment.ts +85 -0
  288. package/src/types/attachmentHandler.ts +31 -0
  289. package/src/types/imageUpload.ts +28 -0
  290. package/src/types/message.ts +1 -0
  291. package/src/utils/chatImageDimens.ts +178 -0
  292. package/src/utils/device.ts +73 -0
  293. package/src/utils/imageUrlOptimizer.ts +56 -0
  294. package/src/utils/imageUtils.ts +76 -0
  295. package/src/utils/resolveMessageType.ts +2 -0
  296. package/src/utils/ui.ts +19 -0
  297. package/src/utils/url.ts +1 -1
  298. package/src/utils/videoThumbnail.ts +85 -0
  299. package/lib/module/screens/chat-detail/ChatList.js +0 -147
  300. package/lib/module/screens/chat-detail/ChatList.js.map +0 -1
  301. package/lib/module/screens/chat-detail/ChatTextBubble.js +0 -62
  302. package/lib/module/screens/chat-detail/ChatTextBubble.js.map +0 -1
  303. package/lib/module/screens/chat-detail/legend/message-types.js +0 -244
  304. package/lib/module/screens/chat-detail/legend/message-types.js.map +0 -1
  305. package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js +0 -24
  306. package/lib/module/screens/chat-detail/messages/ChatMessageBubble.js.map +0 -1
  307. package/lib/module/screens/chat-detail/messages/types.js +0 -4
  308. package/lib/module/screens/chat-detail/messages/types.js.map +0 -1
  309. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts +0 -3
  310. package/lib/typescript/src/screens/chat-detail/ChatList.d.ts.map +0 -1
  311. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts +0 -3
  312. package/lib/typescript/src/screens/chat-detail/ChatTextBubble.d.ts.map +0 -1
  313. package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts +0 -13
  314. package/lib/typescript/src/screens/chat-detail/legend/message-types.d.ts.map +0 -1
  315. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts +0 -3
  316. package/lib/typescript/src/screens/chat-detail/messages/ChatMessageBubble.d.ts.map +0 -1
  317. package/lib/typescript/src/screens/chat-detail/messages/types.d.ts +0 -13
  318. package/lib/typescript/src/screens/chat-detail/messages/types.d.ts.map +0 -1
  319. package/src/screens/chat-detail/ChatList.tsx +0 -190
  320. package/src/screens/chat-detail/ChatTextBubble.tsx +0 -73
  321. package/src/screens/chat-detail/legend/message-types.tsx +0 -304
  322. package/src/screens/chat-detail/messages/ChatMessageBubble.tsx +0 -23
  323. package/src/screens/chat-detail/messages/types.ts +0 -14
@@ -0,0 +1,348 @@
1
+ import { useCallback, useState } from 'react';
2
+ import RNFS from 'react-native-fs';
3
+ import type { Image as PickedImage } from 'react-native-image-crop-picker';
4
+ import DeviceUtils from '../utils/device';
5
+ import UIUtils from '../utils/ui';
6
+ import { trans } from '../translation';
7
+ import { getMimeType, isVideoFromPicker } from '../utils/imageUtils';
8
+ import { ImageUploadAPI } from '../services/imageUpload';
9
+ import VideoThumbnailUtil from '../utils/videoThumbnail';
10
+
11
+ interface ImageWithUploadStatus {
12
+ image: PickedImage;
13
+ uploadProgress: number;
14
+ error: string | null;
15
+ isUploading: boolean;
16
+ uploadedUrl?: string;
17
+ thumbnailUrl?: string;
18
+ thumbnailSize?: number;
19
+ }
20
+
21
+ export interface UseImageAttachmentReturn {
22
+ selectedImages: ImageWithUploadStatus[];
23
+ isImagePreviewVisible: boolean;
24
+ handleImageAttach: () => Promise<void>;
25
+ handleImageRemove: (index: number) => void;
26
+ clearAllImages: () => void;
27
+ }
28
+
29
+ export const useImageAttachment = (): UseImageAttachmentReturn => {
30
+ const [selectedImages, setSelectedImages] = useState<ImageWithUploadStatus[]>(
31
+ []
32
+ );
33
+ const [isImagePreviewVisible, setIsImagePreviewVisible] = useState(false);
34
+
35
+ const updateImageStatus = useCallback(
36
+ (index: number, updates: Partial<Omit<ImageWithUploadStatus, 'image'>>) => {
37
+ setSelectedImages((prev) => {
38
+ const newImages = [...prev];
39
+ if (newImages[index]) {
40
+ newImages[index] = { ...newImages[index], ...updates };
41
+ }
42
+ return newImages;
43
+ });
44
+ },
45
+ []
46
+ );
47
+
48
+ const uploadImage = useCallback(
49
+ async (index: number, image: PickedImage) => {
50
+ console.log('Upload', 'Step 1: Starting upload for image', { index });
51
+ let cachedFilePath: string | null = null;
52
+
53
+ try {
54
+ console.log('Upload', 'Step 2: Updating status to uploading...');
55
+ updateImageStatus(index, {
56
+ isUploading: true,
57
+ error: null,
58
+ uploadProgress: 0,
59
+ });
60
+
61
+ try {
62
+ console.log('Upload', 'Step 3: Preparing file...', {
63
+ originalPath: image.path,
64
+ filename: image.filename,
65
+ });
66
+ const ext = image.filename?.split('.').pop() || 'jpg';
67
+ const fileName = `temp-image-${Date.now()}-${index}.${ext}`;
68
+ cachedFilePath = `${RNFS.DocumentDirectoryPath}/${fileName}`;
69
+
70
+ console.log('Upload', 'Step 4: Copying file to cache...', {
71
+ from: image.path,
72
+ to: cachedFilePath,
73
+ });
74
+ await RNFS.copyFile(image.path, cachedFilePath);
75
+ console.log('Upload', 'Step 5: File copied successfully');
76
+ } catch (fileError: any) {
77
+ console.error('Upload', 'File copy error', fileError);
78
+ throw new Error(
79
+ `Failed to prepare file: ${fileError?.message || 'Unknown error'}`
80
+ );
81
+ }
82
+
83
+ const filename = image.filename || 'image.jpg';
84
+
85
+ const setProgress = (progress: number) => {
86
+ try {
87
+ console.log('Upload', 'Progress', { percentage: progress });
88
+ updateImageStatus(index, { uploadProgress: progress });
89
+ } catch (err) {
90
+ console.error('Upload', 'Progress update error', err);
91
+ }
92
+ };
93
+
94
+ let uploadedImage;
95
+ try {
96
+ console.log('Upload', 'Step 6: Uploading to server...', {
97
+ filename,
98
+ size: image.size,
99
+ });
100
+ uploadedImage = await ImageUploadAPI.uploadImageFile(
101
+ {
102
+ path: cachedFilePath || '',
103
+ filename,
104
+ size: image.size || 0,
105
+ width: image.width || 0,
106
+ height: image.height || 0,
107
+ mime: getMimeType(filename),
108
+ },
109
+ setProgress
110
+ );
111
+ console.log('Upload', 'Step 7: Upload successful!', {
112
+ url: uploadedImage?.url,
113
+ });
114
+ } catch (uploadError: any) {
115
+ console.error('Upload', 'Server upload error', uploadError);
116
+ throw new Error(
117
+ `Upload failed: ${uploadError?.message || 'Unknown error'}`
118
+ );
119
+ }
120
+
121
+ if (!uploadedImage?.url) {
122
+ throw new Error('No URL returned from server');
123
+ }
124
+
125
+ console.log('Upload', 'Step 8: Upload complete, preparing data...');
126
+ updateImageStatus(index, {
127
+ isUploading: false,
128
+ uploadProgress: 100,
129
+ uploadedUrl: uploadedImage.url,
130
+ });
131
+
132
+ console.log(
133
+ 'Upload',
134
+ 'Step 9: Ready to send - waiting for user confirmation'
135
+ );
136
+
137
+ console.log('Upload', 'Step 11: Cleaning up cache file...');
138
+ try {
139
+ if (cachedFilePath) {
140
+ await ImageUploadAPI.cleanupImageFile(cachedFilePath);
141
+ }
142
+ } catch (cleanupError) {
143
+ console.warn('Upload', 'Cleanup warning', cleanupError);
144
+ }
145
+ console.log('Upload', 'Step 12: COMPLETE ✓');
146
+ } catch (error: any) {
147
+ console.error('Upload', 'ERROR', error);
148
+ const errorMsg = error?.message || trans('chat:image_upload_failed');
149
+ updateImageStatus(index, {
150
+ isUploading: false,
151
+ error: errorMsg,
152
+ });
153
+
154
+ try {
155
+ if (cachedFilePath) {
156
+ await ImageUploadAPI.cleanupImageFile(cachedFilePath);
157
+ }
158
+ } catch (cleanupError) {
159
+ console.warn('Upload', 'Cleanup after error', cleanupError);
160
+ }
161
+
162
+ console.error('uploadImage', 'Full error', error);
163
+ }
164
+ },
165
+ [updateImageStatus]
166
+ );
167
+
168
+ const generateVideoThumbnail = useCallback(
169
+ async (index: number, image: PickedImage) => {
170
+ if (!isVideoFromPicker(image)) return; // Only for videos
171
+
172
+ try {
173
+ console.log('[ImageAttach] Generating thumbnail for video:', {
174
+ index,
175
+ filename: image.filename,
176
+ });
177
+
178
+ const videoDuration = (image as any).duration || 0;
179
+ const timeMs = Math.min(1000, Math.floor(videoDuration / 2));
180
+
181
+ // Copy video to safe location
182
+ const ext = image.filename?.split('.').pop() || 'mp4';
183
+ const cachedFileName = `thumb-cache-${Date.now()}-${index}.${ext}`;
184
+ const cachedVideoPath = `${RNFS.DocumentDirectoryPath}/${cachedFileName}`;
185
+
186
+ await RNFS.copyFile(image.path, cachedVideoPath);
187
+
188
+ // Generate thumbnail
189
+ const thumbnail = await VideoThumbnailUtil.generateThumbnail(
190
+ cachedVideoPath,
191
+ { time: timeMs, quality: 80 }
192
+ );
193
+
194
+ if (!thumbnail) throw new Error('Thumbnail generation failed');
195
+
196
+ // Upload thumbnail (force .jpg extension for image file)
197
+ const baseName = image.filename?.split('.')[0] || 'video';
198
+ const uploadedThumbnail = await ImageUploadAPI.uploadImageFile(
199
+ {
200
+ path: thumbnail.path,
201
+ filename: `thumb-${baseName}.jpg`,
202
+ size: 0,
203
+ width: thumbnail.width || 320,
204
+ height: thumbnail.height || 180,
205
+ mime: 'image/jpeg',
206
+ },
207
+ (progress) => {
208
+ console.log('[ImageAttach] Thumbnail upload progress:', {
209
+ index,
210
+ percentage: progress,
211
+ });
212
+ }
213
+ );
214
+
215
+ if (!uploadedThumbnail?.url) {
216
+ throw new Error('No thumbnail URL returned');
217
+ }
218
+
219
+ // Update state with thumbnail
220
+ updateImageStatus(index, {
221
+ thumbnailUrl: uploadedThumbnail.url,
222
+ thumbnailSize: uploadedThumbnail.size || 0,
223
+ });
224
+
225
+ console.log(
226
+ `✅ [ImageAttach] THUMBNAIL URL (picked): ${uploadedThumbnail.url}`
227
+ );
228
+
229
+ // Cleanup cached video
230
+ try {
231
+ await RNFS.unlink(cachedVideoPath);
232
+ } catch (err) {
233
+ console.warn('[ImageAttach] Failed to clean cached video:', err);
234
+ }
235
+ } catch (error) {
236
+ console.error('[ImageAttach] Video thumbnail generation error:', error);
237
+ UIUtils.toast.open({
238
+ title: 'Thumbnail Error',
239
+ message: `Failed to generate thumbnail for ${image.filename}`,
240
+ theme: 'danger',
241
+ });
242
+ }
243
+ },
244
+ [updateImageStatus]
245
+ );
246
+
247
+ const handleImageAttach = useCallback(
248
+ async (multipleMode: boolean = true) => {
249
+ console.log('ImageAttach', 'Step 1: User tapped attachment button', {
250
+ multipleMode,
251
+ });
252
+ try {
253
+ console.log('ImageAttach', 'Step 2: Opening image picker...');
254
+ const images = await DeviceUtils.openImagePicker(multipleMode);
255
+
256
+ if (!images || images.length === 0) {
257
+ console.log('ImageAttach', 'Step 3: User cancelled picker');
258
+ return;
259
+ }
260
+
261
+ console.log('ImageAttach', 'Step 4: Images picked', {
262
+ count: images.length,
263
+ });
264
+
265
+ const MAX_FILE_SIZE = 5 * 1024 * 1024;
266
+ const oversizedImages = images.filter(
267
+ (img) => (img.size || 0) > MAX_FILE_SIZE
268
+ );
269
+
270
+ if (oversizedImages.length > 0) {
271
+ console.warn('ImageAttach', 'Step 5: Some images too large', {
272
+ count: oversizedImages.length,
273
+ maxSize: MAX_FILE_SIZE,
274
+ });
275
+ UIUtils.toast.open({
276
+ title: trans('chat:image_too_large'),
277
+ theme: 'danger',
278
+ });
279
+ const validImages = images.filter(
280
+ (img) => (img.size || 0) <= MAX_FILE_SIZE
281
+ );
282
+ if (validImages.length === 0) return;
283
+ images.splice(0, images.length, ...validImages);
284
+ }
285
+
286
+ console.log('ImageAttach', 'Step 6: Size validation passed');
287
+ const startIndex = selectedImages.length;
288
+
289
+ console.log('ImageAttach', 'Step 7: Adding to preview list', {
290
+ count: images.length,
291
+ startIndex,
292
+ });
293
+
294
+ setSelectedImages((prev) => [
295
+ ...prev,
296
+ ...images.map((image) => ({
297
+ image,
298
+ uploadProgress: 0,
299
+ error: null,
300
+ isUploading: true,
301
+ })),
302
+ ]);
303
+ setIsImagePreviewVisible(true);
304
+
305
+ // Generate thumbnails for videos (parallel)
306
+ console.log(
307
+ 'ImageAttach',
308
+ 'Step 7.5: Generating thumbnails for videos...'
309
+ );
310
+ await Promise.all(
311
+ images.map((image, offset) =>
312
+ generateVideoThumbnail(startIndex + offset, image)
313
+ )
314
+ );
315
+
316
+ console.log('ImageAttach', 'Step 8: Starting uploads...');
317
+ await Promise.all(
318
+ images.map((image, offset) => uploadImage(startIndex + offset, image))
319
+ );
320
+ } catch (error) {
321
+ console.error('ImageAttach', 'ERROR', error);
322
+ UIUtils.toast.open({
323
+ title: trans('chat:image_pick_failed'),
324
+ message: error instanceof Error ? error.message : 'Unknown error',
325
+ theme: 'danger',
326
+ });
327
+ }
328
+ },
329
+ [selectedImages.length, uploadImage, generateVideoThumbnail]
330
+ );
331
+
332
+ const handleImageRemove = useCallback((index: number) => {
333
+ setSelectedImages((prev) => prev.filter((_, i) => i !== index));
334
+ }, []);
335
+
336
+ const clearAllImages = useCallback(() => {
337
+ setSelectedImages([]);
338
+ setIsImagePreviewVisible(false);
339
+ }, []);
340
+
341
+ return {
342
+ selectedImages,
343
+ isImagePreviewVisible,
344
+ handleImageAttach,
345
+ handleImageRemove,
346
+ clearAllImages,
347
+ };
348
+ };
@@ -0,0 +1,32 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ export interface MediaItem {
4
+ url: string;
5
+ isVideo?: boolean;
6
+ }
7
+
8
+ export interface UseMediaViewerReturn {
9
+ visible: boolean;
10
+ mediaList: MediaItem[];
11
+ initIndex: number;
12
+ show: (items: MediaItem[], index?: number) => void;
13
+ hide: () => void;
14
+ }
15
+
16
+ export function useMediaViewer(): UseMediaViewerReturn {
17
+ const [visible, setVisible] = useState(false);
18
+ const [mediaList, setMediaList] = useState<MediaItem[]>([]);
19
+ const [initIndex, setInitIndex] = useState(0);
20
+
21
+ const show = useCallback((items: MediaItem[], index = 0) => {
22
+ setMediaList(items);
23
+ setInitIndex(index);
24
+ setVisible(true);
25
+ }, []);
26
+
27
+ const hide = useCallback(() => {
28
+ setVisible(false);
29
+ }, []);
30
+
31
+ return { visible, mediaList, initIndex, show, hide };
32
+ }
@@ -0,0 +1,295 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { AttachmentOrchestratorService } from '../services/attachmentOrchestrator';
3
+ import type { Attachment, AttachmentSendOptions } from '../types/attachment';
4
+ import { getMimeType } from '../utils/imageUtils';
5
+
6
+ export interface ImageItem {
7
+ type: 'image';
8
+ image: {
9
+ path: string;
10
+ filename?: string;
11
+ size?: number;
12
+ width?: number;
13
+ height?: number;
14
+ };
15
+ uploadProgress: number;
16
+ error: string | null;
17
+ isUploading: boolean;
18
+ uploadedUrl?: string;
19
+ }
20
+
21
+ export interface VideoItem {
22
+ type: 'video';
23
+ video: {
24
+ path: string;
25
+ filename?: string;
26
+ size?: number;
27
+ width?: number;
28
+ height?: number;
29
+ duration?: number;
30
+ };
31
+ uploadProgress: number;
32
+ error: string | null;
33
+ isUploading: boolean;
34
+ uploadedUrl?: string;
35
+ thumbnailUrl?: string;
36
+ thumbnailSize?: number;
37
+ }
38
+
39
+ export interface FileItem {
40
+ type: 'file';
41
+ file: {
42
+ path: string;
43
+ filename?: string;
44
+ size?: number;
45
+ };
46
+ uploadProgress: number;
47
+ error: string | null;
48
+ isUploading: boolean;
49
+ uploadedUrl?: string;
50
+ }
51
+
52
+ export type AttachmentItem = ImageItem | VideoItem | FileItem;
53
+
54
+ export interface UseSendAttachmentReturn {
55
+ isLoading: boolean;
56
+ error: string | null;
57
+ send(
58
+ items: AttachmentItem[],
59
+ options?: Partial<AttachmentSendOptions>,
60
+ onMessageSent?: (message: any) => void
61
+ ): Promise<boolean>;
62
+ clearError(): void;
63
+ }
64
+
65
+ /**
66
+ * Smart hook để send mixed attachments (images + videos + files cùng lúc)
67
+ * - Detect type của từng item
68
+ * - Group by type
69
+ * - Send với handler riêng cho mỗi type
70
+ */
71
+ export function useSendAttachment(): UseSendAttachmentReturn {
72
+ const [isLoading, setIsLoading] = useState(false);
73
+ const [error, setError] = useState<string | null>(null);
74
+
75
+ const send = useCallback(
76
+ async (
77
+ items: AttachmentItem[],
78
+ options?: Partial<AttachmentSendOptions>,
79
+ onMessageSent?: (message: any) => void
80
+ ): Promise<boolean> => {
81
+ console.log('[useSendAttachment] Start sending', {
82
+ totalItems: items.length,
83
+ strategy: options?.strategy || 'individual',
84
+ });
85
+
86
+ // Filter uploaded items
87
+ const uploadedItems = items.filter((item) => item.uploadedUrl);
88
+ console.log('[useSendAttachment] Filtered uploaded items', {
89
+ uploadedCount: uploadedItems.length,
90
+ uploadedTypes: uploadedItems.map((i) => i.type),
91
+ });
92
+
93
+ if (uploadedItems.length === 0) {
94
+ setError('No uploaded attachments to send');
95
+ console.warn('[useSendAttachment] No uploaded items to send');
96
+ return false;
97
+ }
98
+
99
+ setIsLoading(true);
100
+ setError(null);
101
+
102
+ try {
103
+ // Convert items → Attachments (detect type)
104
+ console.log('[useSendAttachment] Converting to Attachment objects...', {
105
+ itemCount: uploadedItems.length,
106
+ itemTypes: uploadedItems.map((i) => i.type),
107
+ });
108
+ const attachments = uploadedItems.map((item) => {
109
+ const baseId = `att-${Date.now()}-${Math.random()}`;
110
+
111
+ // Auto-detect video from mime type if not explicitly typed
112
+ let itemType = item.type;
113
+ if (
114
+ itemType === 'image' &&
115
+ item.uploadedUrl &&
116
+ item.uploadedUrl.includes('.MP4')
117
+ ) {
118
+ itemType = 'video';
119
+ console.log('[useSendAttachment] Auto-detected video from URL');
120
+ }
121
+
122
+ console.log('[useSendAttachment] Converting item:', {
123
+ originalType: item.type,
124
+ detectedType: itemType,
125
+ });
126
+
127
+ if (itemType === 'image' && item.type === 'image') {
128
+ return {
129
+ id: baseId,
130
+ type: 'image' as const,
131
+ sourceFile: {
132
+ path: (item as ImageItem).image.path,
133
+ filename: (item as ImageItem).image.filename || 'image.jpg',
134
+ size: (item as ImageItem).image.size || 0,
135
+ mimeType: getMimeType(
136
+ (item as ImageItem).image.filename || 'jpg'
137
+ ),
138
+ },
139
+ metadata: {
140
+ width: (item as ImageItem).image.width || 0,
141
+ height: (item as ImageItem).image.height || 0,
142
+ },
143
+ };
144
+ }
145
+
146
+ if (itemType === 'video') {
147
+ // Handle both VideoItem and auto-detected video from ImageItem
148
+ const videoData =
149
+ item.type === 'video'
150
+ ? (item as VideoItem).video
151
+ : (item as ImageItem).image;
152
+ const duration =
153
+ item.type === 'video'
154
+ ? (item as VideoItem).video.duration || 0
155
+ : 0;
156
+ const thumbnailUrl =
157
+ item.type === 'video'
158
+ ? (item as VideoItem).thumbnailUrl
159
+ : undefined;
160
+ const thumbnailSize =
161
+ item.type === 'video'
162
+ ? (item as VideoItem).thumbnailSize
163
+ : undefined;
164
+
165
+ const metadata: any = {
166
+ width: videoData.width || 0,
167
+ height: videoData.height || 0,
168
+ duration,
169
+ };
170
+
171
+ if (thumbnailUrl) {
172
+ metadata.thumbnail = {
173
+ url: thumbnailUrl,
174
+ width: videoData.width || 320,
175
+ height: videoData.height || 180,
176
+ size: thumbnailSize,
177
+ };
178
+ }
179
+
180
+ return {
181
+ id: baseId,
182
+ type: 'video' as const,
183
+ sourceFile: {
184
+ path: videoData.path,
185
+ filename: videoData.filename || 'video.mp4',
186
+ size: videoData.size || 0,
187
+ mimeType: getMimeType(videoData.filename || 'mp4'),
188
+ },
189
+ metadata,
190
+ };
191
+ }
192
+
193
+ if (itemType === 'file' && item.type === 'file') {
194
+ return {
195
+ id: baseId,
196
+ type: 'file' as const,
197
+ sourceFile: {
198
+ path: (item as FileItem).file.path,
199
+ filename: (item as FileItem).file.filename || 'file.bin',
200
+ size: (item as FileItem).file.size || 0,
201
+ mimeType: getMimeType(
202
+ (item as FileItem).file.filename || 'bin'
203
+ ),
204
+ },
205
+ metadata: {
206
+ extension:
207
+ (item as FileItem).file.filename?.split('.').pop() || '',
208
+ category: 'other' as const,
209
+ },
210
+ };
211
+ }
212
+
213
+ // Fallback - shouldn't reach here
214
+ throw new Error(`Unknown attachment type: ${itemType}`);
215
+ });
216
+
217
+ // Map uploadedUrl
218
+ console.log('[useSendAttachment] Mapping uploaded URLs...');
219
+ const uploadedUrls: Record<string, string> = {};
220
+ uploadedItems.forEach((item) => {
221
+ let sourcePath = '';
222
+ if (item.type === 'image') sourcePath = item.image.path;
223
+ else if (item.type === 'video') sourcePath = item.video.path;
224
+ else sourcePath = item.file.path;
225
+
226
+ const att = attachments.find((a) => a.sourceFile.path === sourcePath);
227
+ if (att && item.uploadedUrl) {
228
+ uploadedUrls[att.id] = item.uploadedUrl;
229
+ console.log(`[useSendAttachment] Mapped ${att.type}:`, {
230
+ attachmentId: att.id,
231
+ url: item.uploadedUrl,
232
+ });
233
+ }
234
+ });
235
+
236
+ // Send all (mixed types)
237
+ console.log('[useSendAttachment] Calling sendPreparedAttachments...', {
238
+ attachmentCount: attachments.length,
239
+ strategy: options?.strategy || 'individual',
240
+ });
241
+ const result =
242
+ await AttachmentOrchestratorService.sendPreparedAttachments(
243
+ attachments as Attachment[],
244
+ uploadedUrls,
245
+ {
246
+ strategy: 'individual',
247
+ ...options,
248
+ },
249
+ onMessageSent
250
+ );
251
+
252
+ console.log('[useSendAttachment] Send result:', {
253
+ success: result.success,
254
+ sentCount: result.sentAttachmentIds.length,
255
+ failedCount: result.failedAttachmentIds.length,
256
+ mergedMessageId: result.mergedMessageId,
257
+ errors: result.errors,
258
+ });
259
+
260
+ if (!result.success) {
261
+ const errorMsg =
262
+ Object.values(result.errors).join(', ') ||
263
+ 'Failed to send attachments';
264
+ setError(errorMsg);
265
+ console.error('[useSendAttachment] Send failed:', errorMsg);
266
+ return false;
267
+ }
268
+
269
+ console.log('[useSendAttachment] Successfully sent attachments');
270
+ return true;
271
+ } catch (err) {
272
+ const errorMsg = err instanceof Error ? err.message : 'Unknown error';
273
+ setError(errorMsg);
274
+ console.error('[useSendAttachment] Exception:', {
275
+ error: errorMsg,
276
+ stack: err instanceof Error ? err.stack : undefined,
277
+ });
278
+ return false;
279
+ } finally {
280
+ setIsLoading(false);
281
+ console.log('[useSendAttachment] Finished');
282
+ }
283
+ },
284
+ []
285
+ );
286
+
287
+ const clearError = useCallback(() => setError(null), []);
288
+
289
+ return {
290
+ isLoading,
291
+ error,
292
+ send,
293
+ clearError,
294
+ };
295
+ }