@ethora/chat-component-rn 26.5.4 → 26.5.7

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 (238) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +25 -4
  3. package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js +1 -7
  4. package/lib/commonjs/components/ContextMenu/ContextMenuComponents.js.map +1 -1
  5. package/lib/commonjs/components/MainComponents/ChatHeader.js +7 -1
  6. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  7. package/lib/commonjs/components/MainComponents/ChatRoom.js +8 -2
  8. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  9. package/lib/commonjs/components/MainComponents/ChatWrapper.js +2 -1
  10. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  11. package/lib/commonjs/components/MainComponents/MediaMessage.js +11 -5
  12. package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
  13. package/lib/commonjs/components/MainComponents/MessageList.js +10 -2
  14. package/lib/commonjs/components/MainComponents/MessageList.js.map +1 -1
  15. package/lib/commonjs/components/MessageBubble/Message.js +60 -7
  16. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  17. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +1 -38
  18. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  19. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +20 -28
  20. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  21. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +35 -16
  22. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  23. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js +11 -2
  24. package/lib/commonjs/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  25. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +1 -1
  26. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  27. package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js +9 -6
  28. package/lib/commonjs/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
  29. package/lib/commonjs/components/UsersList/Checkbox.js +68 -0
  30. package/lib/commonjs/components/UsersList/Checkbox.js.map +1 -0
  31. package/lib/commonjs/components/UsersList/StyledComponents.js +1 -7
  32. package/lib/commonjs/components/UsersList/StyledComponents.js.map +1 -1
  33. package/lib/commonjs/components/UsersList/UsersList.js +3 -1
  34. package/lib/commonjs/components/UsersList/UsersList.js.map +1 -1
  35. package/lib/commonjs/components/styled/MediaFilePreview.js +96 -5
  36. package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
  37. package/lib/commonjs/components/styled/MessageImage.js +1 -2
  38. package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
  39. package/lib/commonjs/components/styled/SendInput.js +4 -2
  40. package/lib/commonjs/components/styled/SendInput.js.map +1 -1
  41. package/lib/commonjs/components/styled/VideoMessage.js +2 -10
  42. package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
  43. package/lib/commonjs/context/xmppProvider.js +71 -2
  44. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  45. package/lib/commonjs/helpers/mimeToExtension.js +131 -0
  46. package/lib/commonjs/helpers/mimeToExtension.js.map +1 -0
  47. package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js +73 -1
  48. package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js.map +1 -1
  49. package/lib/commonjs/hooks/useChatWrapperInit.js +22 -10
  50. package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
  51. package/lib/commonjs/hooks/useEventHandlers.js +9 -1
  52. package/lib/commonjs/hooks/useEventHandlers.js.map +1 -1
  53. package/lib/commonjs/hooks/useMessageHeapState.js +6 -2
  54. package/lib/commonjs/hooks/useMessageHeapState.js.map +1 -1
  55. package/lib/commonjs/hooks/useSendMessage.js +48 -4
  56. package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
  57. package/lib/commonjs/networking/api-requests/auth.api.js +7 -1
  58. package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
  59. package/lib/commonjs/networking/xmppClient.js +87 -1
  60. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  61. package/lib/commonjs/roomStore/roomHeapSlice.js +26 -8
  62. package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
  63. package/lib/module/components/ContextMenu/ContextMenuComponents.js +0 -6
  64. package/lib/module/components/ContextMenu/ContextMenuComponents.js.map +1 -1
  65. package/lib/module/components/MainComponents/ChatHeader.js +7 -1
  66. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  67. package/lib/module/components/MainComponents/ChatRoom.js +8 -2
  68. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  69. package/lib/module/components/MainComponents/ChatWrapper.js +2 -1
  70. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  71. package/lib/module/components/MainComponents/MediaMessage.js +11 -5
  72. package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
  73. package/lib/module/components/MainComponents/MessageList.js +11 -3
  74. package/lib/module/components/MainComponents/MessageList.js.map +1 -1
  75. package/lib/module/components/MessageBubble/Message.js +60 -7
  76. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  77. package/lib/module/components/MessageBubble/MessageInteractions.js +3 -40
  78. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  79. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +21 -29
  80. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  81. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +36 -17
  82. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  83. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js +11 -2
  84. package/lib/module/components/Modals/ModalWrapper/ModalWrapper.js.map +1 -1
  85. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +1 -1
  86. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  87. package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js +9 -6
  88. package/lib/module/components/RoomComponents/styled/LastMessageEmoji.js.map +1 -1
  89. package/lib/module/components/UsersList/Checkbox.js +63 -0
  90. package/lib/module/components/UsersList/Checkbox.js.map +1 -0
  91. package/lib/module/components/UsersList/StyledComponents.js +0 -6
  92. package/lib/module/components/UsersList/StyledComponents.js.map +1 -1
  93. package/lib/module/components/UsersList/UsersList.js +2 -1
  94. package/lib/module/components/UsersList/UsersList.js.map +1 -1
  95. package/lib/module/components/styled/MediaFilePreview.js +97 -6
  96. package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
  97. package/lib/module/components/styled/MessageImage.js +1 -2
  98. package/lib/module/components/styled/MessageImage.js.map +1 -1
  99. package/lib/module/components/styled/SendInput.js +4 -2
  100. package/lib/module/components/styled/SendInput.js.map +1 -1
  101. package/lib/module/components/styled/VideoMessage.js +2 -10
  102. package/lib/module/components/styled/VideoMessage.js.map +1 -1
  103. package/lib/module/context/xmppProvider.js +73 -3
  104. package/lib/module/context/xmppProvider.js.map +1 -1
  105. package/lib/module/helpers/mimeToExtension.js +124 -0
  106. package/lib/module/helpers/mimeToExtension.js.map +1 -0
  107. package/lib/module/helpers/resolveInitBeforeLoadUser.js +71 -0
  108. package/lib/module/helpers/resolveInitBeforeLoadUser.js.map +1 -1
  109. package/lib/module/hooks/useChatWrapperInit.js +22 -10
  110. package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
  111. package/lib/module/hooks/useEventHandlers.js +9 -1
  112. package/lib/module/hooks/useEventHandlers.js.map +1 -1
  113. package/lib/module/hooks/useMessageHeapState.js +6 -2
  114. package/lib/module/hooks/useMessageHeapState.js.map +1 -1
  115. package/lib/module/hooks/useSendMessage.js +49 -5
  116. package/lib/module/hooks/useSendMessage.js.map +1 -1
  117. package/lib/module/networking/api-requests/auth.api.js +7 -1
  118. package/lib/module/networking/api-requests/auth.api.js.map +1 -1
  119. package/lib/module/networking/xmppClient.js +87 -1
  120. package/lib/module/networking/xmppClient.js.map +1 -1
  121. package/lib/module/roomStore/roomHeapSlice.js +24 -7
  122. package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
  123. package/lib/typescript/components/ContextMenu/ContextMenuComponents.d.ts +0 -1
  124. package/lib/typescript/components/ContextMenu/ContextMenuComponents.d.ts.map +1 -1
  125. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  126. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  127. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  128. package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
  129. package/lib/typescript/components/MainComponents/MessageList.d.ts.map +1 -1
  130. package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
  131. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  132. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  133. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  134. package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts +6 -0
  135. package/lib/typescript/components/Modals/ModalWrapper/ModalWrapper.d.ts.map +1 -1
  136. package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts +8 -0
  137. package/lib/typescript/components/RoomComponents/styled/LastMessageEmoji.d.ts.map +1 -1
  138. package/lib/typescript/components/UsersList/Checkbox.d.ts +16 -0
  139. package/lib/typescript/components/UsersList/Checkbox.d.ts.map +1 -0
  140. package/lib/typescript/components/UsersList/StyledComponents.d.ts +0 -1
  141. package/lib/typescript/components/UsersList/StyledComponents.d.ts.map +1 -1
  142. package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
  143. package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
  144. package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
  145. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  146. package/lib/typescript/helpers/mimeToExtension.d.ts +37 -0
  147. package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -0
  148. package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts +18 -0
  149. package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts.map +1 -1
  150. package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
  151. package/lib/typescript/hooks/useEventHandlers.d.ts +6 -0
  152. package/lib/typescript/hooks/useEventHandlers.d.ts.map +1 -1
  153. package/lib/typescript/hooks/useMessageHeapState.d.ts +2 -0
  154. package/lib/typescript/hooks/useMessageHeapState.d.ts.map +1 -1
  155. package/lib/typescript/hooks/useSendMessage.d.ts +2 -1
  156. package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
  157. package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
  158. package/lib/typescript/networking/xmppClient.d.ts +25 -1
  159. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  160. package/lib/typescript/roomStore/roomHeapSlice.d.ts +22 -1
  161. package/lib/typescript/roomStore/roomHeapSlice.d.ts.map +1 -1
  162. package/lib/typescript/types/types.d.ts +6 -0
  163. package/lib/typescript/types/types.d.ts.map +1 -1
  164. package/metro.js +17 -57
  165. package/package.json +1 -1
  166. package/src/components/ContextMenu/ContextMenuComponents.tsx +0 -7
  167. package/src/components/MainComponents/ChatHeader.tsx +5 -1
  168. package/src/components/MainComponents/ChatRoom.tsx +6 -1
  169. package/src/components/MainComponents/ChatWrapper.tsx +1 -0
  170. package/src/components/MainComponents/MediaMessage.tsx +15 -8
  171. package/src/components/MainComponents/MessageList.tsx +10 -3
  172. package/src/components/MessageBubble/Message.tsx +64 -10
  173. package/src/components/MessageBubble/MessageInteractions.tsx +2 -105
  174. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +20 -41
  175. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +30 -12
  176. package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +22 -1
  177. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +1 -1
  178. package/src/components/RoomComponents/styled/LastMessageEmoji.tsx +9 -7
  179. package/src/components/UsersList/Checkbox.tsx +68 -0
  180. package/src/components/UsersList/StyledComponents.ts +0 -8
  181. package/src/components/UsersList/UsersList.tsx +1 -1
  182. package/src/components/styled/MediaFilePreview.tsx +91 -5
  183. package/src/components/styled/MessageImage.tsx +1 -2
  184. package/src/components/styled/SendInput.tsx +3 -0
  185. package/src/components/styled/VideoMessage.tsx +3 -10
  186. package/src/context/xmppProvider.tsx +90 -2
  187. package/src/helpers/mimeToExtension.ts +135 -0
  188. package/src/helpers/resolveInitBeforeLoadUser.ts +79 -0
  189. package/src/hooks/useChatWrapperInit.ts +51 -46
  190. package/src/hooks/useEventHandlers.tsx +24 -1
  191. package/src/hooks/useMessageHeapState.tsx +6 -2
  192. package/src/hooks/useSendMessage.tsx +75 -4
  193. package/src/networking/api-requests/auth.api.ts +5 -1
  194. package/src/networking/xmppClient.ts +95 -1
  195. package/src/roomStore/roomHeapSlice.ts +47 -4
  196. package/src/types/types.ts +10 -0
  197. package/lib/commonjs/components/InputComponents/AudioInput.js +0 -23
  198. package/lib/commonjs/components/InputComponents/AudioInput.js.map +0 -1
  199. package/lib/commonjs/components/InputComponents/AudioRecorder.js +0 -101
  200. package/lib/commonjs/components/InputComponents/AudioRecorder.js.map +0 -1
  201. package/lib/commonjs/components/InputComponents/MediaInput.js +0 -98
  202. package/lib/commonjs/components/InputComponents/MediaInput.js.map +0 -1
  203. package/lib/commonjs/components/InputComponents/RecordingIndicator.js +0 -70
  204. package/lib/commonjs/components/InputComponents/RecordingIndicator.js.map +0 -1
  205. package/lib/commonjs/components/Modals/ModalSelectMedia/ModalSelectMedia.js +0 -207
  206. package/lib/commonjs/components/Modals/ModalSelectMedia/ModalSelectMedia.js.map +0 -1
  207. package/lib/commonjs/components/OperationalModal/OperationalModal.js +0 -83
  208. package/lib/commonjs/components/OperationalModal/OperationalModal.js.map +0 -1
  209. package/lib/module/components/InputComponents/AudioInput.js +0 -18
  210. package/lib/module/components/InputComponents/AudioInput.js.map +0 -1
  211. package/lib/module/components/InputComponents/AudioRecorder.js +0 -95
  212. package/lib/module/components/InputComponents/AudioRecorder.js.map +0 -1
  213. package/lib/module/components/InputComponents/MediaInput.js +0 -92
  214. package/lib/module/components/InputComponents/MediaInput.js.map +0 -1
  215. package/lib/module/components/InputComponents/RecordingIndicator.js +0 -65
  216. package/lib/module/components/InputComponents/RecordingIndicator.js.map +0 -1
  217. package/lib/module/components/Modals/ModalSelectMedia/ModalSelectMedia.js +0 -200
  218. package/lib/module/components/Modals/ModalSelectMedia/ModalSelectMedia.js.map +0 -1
  219. package/lib/module/components/OperationalModal/OperationalModal.js +0 -77
  220. package/lib/module/components/OperationalModal/OperationalModal.js.map +0 -1
  221. package/lib/typescript/components/InputComponents/AudioInput.d.ts +0 -9
  222. package/lib/typescript/components/InputComponents/AudioInput.d.ts.map +0 -1
  223. package/lib/typescript/components/InputComponents/AudioRecorder.d.ts +0 -9
  224. package/lib/typescript/components/InputComponents/AudioRecorder.d.ts.map +0 -1
  225. package/lib/typescript/components/InputComponents/MediaInput.d.ts +0 -16
  226. package/lib/typescript/components/InputComponents/MediaInput.d.ts.map +0 -1
  227. package/lib/typescript/components/InputComponents/RecordingIndicator.d.ts +0 -4
  228. package/lib/typescript/components/InputComponents/RecordingIndicator.d.ts.map +0 -1
  229. package/lib/typescript/components/Modals/ModalSelectMedia/ModalSelectMedia.d.ts +0 -9
  230. package/lib/typescript/components/Modals/ModalSelectMedia/ModalSelectMedia.d.ts.map +0 -1
  231. package/lib/typescript/components/OperationalModal/OperationalModal.d.ts +0 -9
  232. package/lib/typescript/components/OperationalModal/OperationalModal.d.ts.map +0 -1
  233. package/src/components/InputComponents/AudioInput.tsx +0 -24
  234. package/src/components/InputComponents/AudioRecorder.tsx +0 -100
  235. package/src/components/InputComponents/MediaInput.tsx +0 -113
  236. package/src/components/InputComponents/RecordingIndicator.tsx +0 -77
  237. package/src/components/Modals/ModalSelectMedia/ModalSelectMedia.tsx +0 -283
  238. package/src/components/OperationalModal/OperationalModal.tsx +0 -78
@@ -23,10 +23,17 @@ interface MessageEditedEvent {
23
23
  user: any;
24
24
  }
25
25
 
26
+ interface MessageRetryEvent {
27
+ messageId: string;
28
+ roomJID: string;
29
+ messageType: 'text' | 'media';
30
+ }
31
+
26
32
  export interface EventHandlersHook {
27
33
  handleMessageSent: (event: MessageSentEvent) => Promise<void>;
28
34
  handleMessageFailed: (event: MessageFailedEvent) => void;
29
35
  handleMessageEdited: (event: MessageEditedEvent) => void;
36
+ handleMessageRetry: (event: MessageRetryEvent) => void;
30
37
  }
31
38
 
32
39
  export const useEventHandlers = (config?: IConfig): EventHandlersHook => {
@@ -77,5 +84,21 @@ export const useEventHandlers = (config?: IConfig): EventHandlersHook => {
77
84
  [config]
78
85
  );
79
86
 
80
- return { handleMessageSent, handleMessageFailed, handleMessageEdited };
87
+ const handleMessageRetry = useCallback(
88
+ (event: MessageRetryEvent) => {
89
+ try {
90
+ config?.eventHandlers?.onMessageRetry?.(event);
91
+ } catch (error) {
92
+ console.error('Error in message retry handler:', error);
93
+ }
94
+ },
95
+ [config]
96
+ );
97
+
98
+ return {
99
+ handleMessageSent,
100
+ handleMessageFailed,
101
+ handleMessageEdited,
102
+ handleMessageRetry,
103
+ };
81
104
  };
@@ -3,9 +3,13 @@ import { RootState } from '../roomStore';
3
3
 
4
4
  export const useMessageHeapState = () => {
5
5
  const queue = useSelector(
6
- (state: RootState) => state.roomHeapSlice.messageHeap
6
+ (state: RootState) => state.roomHeapSlice?.messageHeap
7
+ );
8
+ const failedMessages = useSelector(
9
+ (state: RootState) => state.roomHeapSlice?.failedMessages
7
10
  );
8
11
  const idSet = new Set(queue?.map((m) => m.id) ?? []);
12
+ const failedIdSet = new Set(Object.keys(failedMessages || {}));
9
13
 
10
- return { queue, idSet };
14
+ return { queue, idSet, failedMessages, failedIdSet };
11
15
  };
@@ -3,7 +3,11 @@ import { v4 as uuidv4 } from 'uuid';
3
3
  import { useXmppClient } from '../context/xmppProvider';
4
4
  import { useDispatch, useSelector } from 'react-redux';
5
5
  import { addRoomMessage, setEditAction } from '../roomStore/roomsSlice';
6
- import { addMessageToHeap } from '../roomStore/roomHeapSlice';
6
+ import {
7
+ addMessageToHeap,
8
+ clearMessageFailure,
9
+ markMessageFailed,
10
+ } from '../roomStore/roomHeapSlice';
7
11
  import { uploadFile } from '../networking/api-requests/auth.api';
8
12
  import { RootState } from '../roomStore';
9
13
  import { useEventHandlers } from './useEventHandlers';
@@ -38,8 +42,13 @@ export const useSendMessage = (_configOverride?: IConfig) => {
38
42
  handleMessageSent,
39
43
  handleMessageFailed,
40
44
  handleMessageEdited,
45
+ handleMessageRetry,
41
46
  } = useEventHandlers(config);
42
47
 
48
+ const failedMessages = useSelector(
49
+ (state: RootState) => state.roomHeapSlice?.failedMessages || {}
50
+ );
51
+
43
52
  const sendMessage = useCallback(
44
53
  async (
45
54
  message: string,
@@ -140,6 +149,17 @@ export const useSendMessage = (_configOverride?: IConfig) => {
140
149
  messageType: 'text',
141
150
  });
142
151
  } catch (error) {
152
+ dispatch(
153
+ markMessageFailed({
154
+ kind: 'text',
155
+ id: optimisticId,
156
+ roomJID: activeRoomJID,
157
+ body: message,
158
+ isReply,
159
+ isChecked,
160
+ mainMessage,
161
+ })
162
+ );
143
163
  handleMessageFailed({
144
164
  message,
145
165
  roomJID: activeRoomJID,
@@ -158,11 +178,12 @@ export const useSendMessage = (_configOverride?: IConfig) => {
158
178
  activeRoomJID: string,
159
179
  isReply: boolean = false,
160
180
  isChecked: boolean = false,
161
- mainMessage: string = ''
181
+ mainMessage: string = '',
182
+ existingId?: string
162
183
  ) => {
163
184
  client?.onCriticalSend?.(activeRoomJID);
164
185
 
165
- const id = `send-media-message:${uuidv4()}`;
186
+ const id = existingId || `send-media-message:${uuidv4()}`;
166
187
  const optimisticTimestamp = Date.now();
167
188
  const optimisticDate = new Date(optimisticTimestamp).toISOString();
168
189
  const selfId =
@@ -171,7 +192,7 @@ export const useSendMessage = (_configOverride?: IConfig) => {
171
192
  `${user?.firstName || ''} ${user?.lastName || ''}`.trim() || selfId;
172
193
  const fileSizeStr = data?.size != null ? String(data.size) : '';
173
194
 
174
- if (!config?.disableSentLogic) {
195
+ if (!config?.disableSentLogic && !existingId) {
175
196
  dispatch(
176
197
  addRoomMessage({
177
198
  roomJID: activeRoomJID,
@@ -290,6 +311,18 @@ export const useSendMessage = (_configOverride?: IConfig) => {
290
311
  requestUrl: error?.config?.url,
291
312
  axiosMessage: error?.message,
292
313
  });
314
+ dispatch(
315
+ markMessageFailed({
316
+ kind: 'media',
317
+ id,
318
+ roomJID: activeRoomJID,
319
+ data,
320
+ type,
321
+ isReply,
322
+ isChecked,
323
+ mainMessage,
324
+ })
325
+ );
293
326
  handleMessageFailed({
294
327
  message: 'media',
295
328
  roomJID: activeRoomJID,
@@ -301,6 +334,43 @@ export const useSendMessage = (_configOverride?: IConfig) => {
301
334
  [client, config, user, dispatch, handleMessageSent, handleMessageFailed]
302
335
  );
303
336
 
337
+ // Retry a previously-failed send by replaying the saved payload from
338
+ // the heap. Clears the failure flag first so the bubble flips back to
339
+ // "sending..."; if the retry fails again `markMessageFailed` re-arms
340
+ // it. No-op when the id isn't in the failed map.
341
+ const retryMessage = useCallback(
342
+ async (failedId: string) => {
343
+ const payload = failedMessages[failedId];
344
+ if (!payload) {return;}
345
+ dispatch(clearMessageFailure(failedId));
346
+ handleMessageRetry({
347
+ messageId: failedId,
348
+ roomJID: payload.roomJID,
349
+ messageType: payload.kind,
350
+ });
351
+ if (payload.kind === 'text') {
352
+ await sendMessage(
353
+ payload.body,
354
+ payload.roomJID,
355
+ payload.isReply,
356
+ payload.isChecked,
357
+ payload.mainMessage
358
+ );
359
+ } else {
360
+ await sendMedia(
361
+ payload.data,
362
+ payload.type,
363
+ payload.roomJID,
364
+ payload.isReply,
365
+ payload.isChecked,
366
+ payload.mainMessage,
367
+ failedId
368
+ );
369
+ }
370
+ },
371
+ [failedMessages, dispatch, handleMessageRetry, sendMessage, sendMedia]
372
+ );
373
+
304
374
  // ChatRoom/ThreadWrapper consume this as the "edit branch" of send.
305
375
  // It mirrors the edit path inside `sendMessage` so callers can wire it
306
376
  // up directly when `editAction.isEdit` is true.
@@ -355,6 +425,7 @@ export const useSendMessage = (_configOverride?: IConfig) => {
355
425
  sendMessage,
356
426
  sendMedia,
357
427
  sendEditMessage,
428
+ retryMessage,
358
429
  isLastMessageFromUserAndProcessing,
359
430
  };
360
431
  };
@@ -134,7 +134,11 @@ export function uploadFile(formData: FormData) {
134
134
  headers: {
135
135
  Authorization: token,
136
136
  Accept: '*/*',
137
- 'Content-Type': 'multipart/form-data',
137
+ 'Content-Type': null,
138
+ },
139
+ transformRequest: (data: any, headers: any) => {
140
+ if (headers?.delete) {headers.delete('Content-Type');}
141
+ return data;
138
142
  },
139
143
  });
140
144
  }
@@ -26,6 +26,25 @@ import { pushLog as devPushLog } from '../utils/devLogger';
26
26
  // Canonical production XMPP WSS endpoint (standard wss/443, no port suffix).
27
27
  const DEFAULT_DEV_SERVER = 'xmpp.chat.ethora.com';
28
28
 
29
+ // @xmpp/client surfaces SASL/stream errors in a few shapes depending on
30
+ // where it fails — XMPPError on stream:error, SASLError on SASL bind, or
31
+ // a generic Error whose message contains "not-authorized". Match all of
32
+ // them so we trigger the credentials refresh path consistently.
33
+ function isNotAuthorizedError(err: any): boolean {
34
+ if (!err) {return false;}
35
+ const condition =
36
+ err?.condition ||
37
+ err?.name ||
38
+ err?.type ||
39
+ err?.error?.condition ||
40
+ '';
41
+ if (typeof condition === 'string' && condition.includes('not-authorized')) {
42
+ return true;
43
+ }
44
+ const message = (err?.message || err?.text || '').toString();
45
+ return /not-authorized|SASLError/i.test(message);
46
+ }
47
+
29
48
  type HistorySource = 'active' | 'send_ack' | 'background' | 'default';
30
49
 
31
50
  interface HistoryOptions {
@@ -40,6 +59,11 @@ interface MamInFlightEntry {
40
59
  startedAt: number;
41
60
  }
42
61
 
62
+ export type XmppCredentialsProvider = () => Promise<{
63
+ username: string;
64
+ password: string;
65
+ }>;
66
+
43
67
  export class XmppClient {
44
68
  client!: Client;
45
69
  xmppSettings?: xmppSettingsInterface;
@@ -56,6 +80,13 @@ export class XmppClient {
56
80
  reconnectDelay = 2000;
57
81
  suppressReconnect = false;
58
82
 
83
+ // Set when SASL fails with `not-authorized` (typically a stale JWT-
84
+ // derived XMPP password after idle). Triggers the credentialsProvider
85
+ // refresh path in reconnect().
86
+ lastAuthError: 'not-authorized' | null = null;
87
+ private credentialsProvider: XmppCredentialsProvider | null = null;
88
+ private credentialsRefreshInFlight: Promise<void> | null = null;
89
+
59
90
  // ---- QoS state (mirrors web XmppClient) ----------------------------
60
91
  presencesReady = false;
61
92
  disableLastRead = false;
@@ -74,6 +105,23 @@ export class XmppClient {
74
105
  return this.client && this.client.status === 'online';
75
106
  }
76
107
 
108
+ /**
109
+ * Inject (or replace) the callback used by reconnect() to fetch fresh
110
+ * XMPP credentials after a `not-authorized` SASL failure. The provider
111
+ * is responsible for refreshing whatever upstream token is needed
112
+ * (REST JWT, /users/client, etc.) and returning the resulting
113
+ * username + password.
114
+ */
115
+ setCredentialsProvider(provider: XmppCredentialsProvider | null) {
116
+ this.credentialsProvider = provider;
117
+ }
118
+
119
+ /** Swap in fresh credentials before the next reconnect. */
120
+ updateCredentials(username: string, password: string) {
121
+ this.username = username;
122
+ this.password = password;
123
+ }
124
+
77
125
  constructor(
78
126
  username: string,
79
127
  password: string,
@@ -285,6 +333,9 @@ export class XmppClient {
285
333
  this.attachEventListeners();
286
334
  this.client.start().catch((error: any) => {
287
335
  console.error('Error starting xmpp client:', error);
336
+ if (isNotAuthorizedError(error)) {
337
+ this.lastAuthError = 'not-authorized';
338
+ }
288
339
  this.status = 'error';
289
340
  });
290
341
  } catch (error) {
@@ -320,6 +371,9 @@ export class XmppClient {
320
371
 
321
372
  this.client.on('error', (error: any) => {
322
373
  console.error('XMPP client error:', error);
374
+ if (isNotAuthorizedError(error)) {
375
+ this.lastAuthError = 'not-authorized';
376
+ }
323
377
  try {
324
378
  devPushLog(
325
379
  'xmpp',
@@ -385,9 +439,25 @@ export class XmppClient {
385
439
  setTimeout(() => this.reconnect(), delay);
386
440
  }
387
441
 
388
- reconnect() {
442
+ async reconnect() {
389
443
  if (this.suppressReconnect) {return;}
390
444
  console.log('Attempting to reconnect xmpp client...');
445
+
446
+ // If the last failure was a SASL `not-authorized` (typically a stale
447
+ // JWT-derived XMPP password after idle), try to fetch fresh creds
448
+ // before retrying. Without this, reconnect retries with the same
449
+ // stale password forever and the user has to kill the app.
450
+ if (this.lastAuthError === 'not-authorized' && this.credentialsProvider) {
451
+ try {
452
+ await this.refreshCredentialsOnce();
453
+ } catch (err) {
454
+ console.warn(
455
+ 'XMPP credential refresh failed; reconnecting with cached creds',
456
+ err
457
+ );
458
+ }
459
+ }
460
+
391
461
  if (this.client) {
392
462
  this.client.stop().finally(() => {
393
463
  this.initializeClient();
@@ -397,6 +467,30 @@ export class XmppClient {
397
467
  }
398
468
  }
399
469
 
470
+ /**
471
+ * Single-flight wrapper around credentialsProvider. Multiple concurrent
472
+ * reconnect() calls (e.g. from the provider's reconnect effect firing
473
+ * twice quickly) share one in-flight request and update `this.password`
474
+ * exactly once.
475
+ */
476
+ private refreshCredentialsOnce(): Promise<void> {
477
+ if (this.credentialsRefreshInFlight) {return this.credentialsRefreshInFlight;}
478
+ if (!this.credentialsProvider) {return Promise.resolve();}
479
+ const provider = this.credentialsProvider;
480
+ this.credentialsRefreshInFlight = (async () => {
481
+ try {
482
+ const fresh = await provider();
483
+ if (fresh?.username && fresh?.password) {
484
+ this.updateCredentials(fresh.username, fresh.password);
485
+ this.lastAuthError = null;
486
+ }
487
+ } finally {
488
+ this.credentialsRefreshInFlight = null;
489
+ }
490
+ })();
491
+ return this.credentialsRefreshInFlight;
492
+ }
493
+
400
494
  async disconnect(options?: { suppressReconnect?: boolean }): Promise<void> {
401
495
  if (options?.suppressReconnect) {this.suppressReconnect = true;}
402
496
  return this.close();
@@ -2,18 +2,43 @@ import { createSlice, PayloadAction, type Slice } from '@reduxjs/toolkit';
2
2
  import type { WritableDraft } from 'immer';
3
3
  import { IMessage } from '../types/types';
4
4
 
5
+ // Saved alongside a failed-message id so the retry button can resend
6
+ // without the bubble needing to remember the original draft itself.
7
+ // `kind === 'text'` carries the plain body; `kind === 'media'` carries
8
+ // the upload payload so we don't have to make the user re-pick the file.
9
+ export type FailedMessagePayload =
10
+ | {
11
+ kind: 'text';
12
+ id: string;
13
+ roomJID: string;
14
+ body: string;
15
+ isReply?: boolean;
16
+ isChecked?: boolean;
17
+ mainMessage?: string;
18
+ }
19
+ | {
20
+ kind: 'media';
21
+ id: string;
22
+ roomJID: string;
23
+ data: any;
24
+ type: string;
25
+ isReply?: boolean;
26
+ isChecked?: boolean;
27
+ mainMessage?: string;
28
+ };
29
+
5
30
  export interface roomHeapSliceState {
6
31
  messageHeap: IMessage[];
32
+ // Keyed by message id so the bubble's `isFailed = failedMessages[id]`
33
+ // check is O(1). Carries enough payload data to actually retry.
34
+ failedMessages: Record<string, FailedMessagePayload>;
7
35
  }
8
36
 
9
37
  const initialState: roomHeapSliceState = {
10
38
  messageHeap: [],
39
+ failedMessages: {},
11
40
  };
12
41
 
13
- // Reducers extracted as a named const so we can give the slice an
14
- // explicit Slice<State, typeof reducers, Name> type. Without that, tsc's
15
- // declaration emission inlines immer's internal WritableNonArrayDraft
16
- // type and fails with TS4023.
17
42
  const reducers = {
18
43
  addMessageToHeap: (state: WritableDraft<roomHeapSliceState>, action: PayloadAction<IMessage>) => {
19
44
  state.messageHeap.push(action.payload);
@@ -26,6 +51,22 @@ const reducers = {
26
51
  },
27
52
  clearHeap: (state: WritableDraft<roomHeapSliceState>) => {
28
53
  state.messageHeap = [];
54
+ state.failedMessages = {};
55
+ },
56
+ markMessageFailed: (
57
+ state: WritableDraft<roomHeapSliceState>,
58
+ action: PayloadAction<FailedMessagePayload>
59
+ ) => {
60
+ state.failedMessages[action.payload.id] = action.payload;
61
+ // Drop from the pending heap — the bubble looks at both, and
62
+ // showing "sending..." alongside a failure indicator is confusing.
63
+ const index = state.messageHeap.findIndex((m) => m.id === action.payload.id);
64
+ if (index !== -1) {
65
+ state.messageHeap.splice(index, 1);
66
+ }
67
+ },
68
+ clearMessageFailure: (state: WritableDraft<roomHeapSliceState>, action: PayloadAction<string>) => {
69
+ delete state.failedMessages[action.payload];
29
70
  },
30
71
  };
31
72
 
@@ -39,6 +80,8 @@ export const {
39
80
  addMessageToHeap,
40
81
  clearHeap,
41
82
  removeMessageFromHeapById,
83
+ markMessageFailed,
84
+ clearMessageFailure,
42
85
  } = roomHeapSlice.actions;
43
86
 
44
87
  export default roomHeapSlice.reducer;
@@ -249,6 +249,11 @@ export interface IConfig {
249
249
  disableHeader?: boolean;
250
250
  disableMedia?: boolean;
251
251
  chatHeaderBurgerMenu?: boolean;
252
+ // Hide the burger icon entirely (the icon that opens the chatHeader-
253
+ // BurgerMenu dropdown). `chatHeaderBurgerMenu` controls dropdown
254
+ // visibility only — set this flag when you want neither the icon nor
255
+ // the dropdown to render (e.g. patient-facing apps with a single room).
256
+ disableChatHeaderBurgerMenuIcon?: boolean;
252
257
  chatHeaderAdditional?: { enabled: boolean; element: any };
253
258
  headerMenu?: () => void;
254
259
  headerChatMenu?: () => void;
@@ -420,6 +425,11 @@ export interface IConfig {
420
425
  roomJID: string;
421
426
  user: any;
422
427
  }) => void;
428
+ onMessageRetry?: (event: {
429
+ messageId: string;
430
+ roomJID: string;
431
+ messageType: 'text' | 'media';
432
+ }) => void;
423
433
  };
424
434
  }
425
435
 
@@ -1,23 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _react = _interopRequireDefault(require("react"));
8
- var _AudioRecorder = _interopRequireDefault(require("../InputComponents/AudioRecorder"));
9
- var _jsxRuntime = require("react/jsx-runtime");
10
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
- const AudioInput = ({
12
- isRecording,
13
- setIsRecording,
14
- handleSendClick
15
- }) => {
16
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_AudioRecorder.default, {
17
- setIsRecording: setIsRecording,
18
- isRecording: isRecording,
19
- handleSendClick: handleSendClick
20
- });
21
- };
22
- var _default = exports.default = AudioInput;
23
- //# sourceMappingURL=AudioInput.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_react","_interopRequireDefault","require","_AudioRecorder","_jsxRuntime","e","__esModule","default","AudioInput","isRecording","setIsRecording","handleSendClick","jsx","_default","exports"],"sourceRoot":"../../../../src","sources":["components/InputComponents/AudioInput.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,cAAA,GAAAF,sBAAA,CAAAC,OAAA;AAA6D,IAAAE,WAAA,GAAAF,OAAA;AAAA,SAAAD,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAQ7D,MAAMG,UAAqC,GAAGA,CAAC;EAC7CC,WAAW;EACXC,cAAc;EACdC;AACF,CAAC,KAAK;EACJ,oBACE,IAAAP,WAAA,CAAAQ,GAAA,EAACT,cAAA,CAAAI,OAAa;IACZG,cAAc,EAAEA,cAAe;IAC/BD,WAAW,EAAEA,WAAY;IACzBE,eAAe,EAAEA;EAAgB,CAClC,CAAC;AAEN,CAAC;AAAC,IAAAE,QAAA,GAAAC,OAAA,CAAAP,OAAA,GAEaC,UAAU","ignoreList":[]}
@@ -1,101 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _react = _interopRequireWildcard(require("react"));
8
- var _StyledInputComponents = require("../styled/StyledInputComponents/StyledInputComponents");
9
- var _icons = require("../../assets/icons");
10
- var _Button = _interopRequireDefault(require("../styled/Button"));
11
- var _RecordingIndicator = _interopRequireDefault(require("./RecordingIndicator"));
12
- var _reactNative = require("react-native");
13
- var _reactNativeAudioRecorderPlayer = _interopRequireDefault(require("react-native-audio-recorder-player"));
14
- var _reactNativeFs = _interopRequireDefault(require("react-native-fs"));
15
- var _jsxRuntime = require("react/jsx-runtime");
16
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
18
- const AudioRecorder = ({
19
- setIsRecording,
20
- isRecording,
21
- handleSendClick
22
- }) => {
23
- const audioRecorderPlayer = (0, _react.useRef)(new _reactNativeAudioRecorderPlayer.default()).current;
24
- const [timer, setTimer] = (0, _react.useState)('00:00');
25
- const [audioPath, setAudioPath] = (0, _react.useState)(null);
26
- (0, _react.useEffect)(() => {
27
- return () => {
28
- audioRecorderPlayer.stopRecorder();
29
- audioRecorderPlayer.removeRecordBackListener();
30
- };
31
- }, [audioRecorderPlayer]);
32
- const startRecording = async () => {
33
- try {
34
- const path = _reactNative.Platform.select({
35
- ios: `${_reactNativeFs.default.DocumentDirectoryPath}/recording.m4a`,
36
- android: `${_reactNativeFs.default.CachesDirectoryPath}/recording.mp4`
37
- });
38
- const uri = await audioRecorderPlayer.startRecorder(path);
39
- setAudioPath(uri);
40
- audioRecorderPlayer.addRecordBackListener(e => {
41
- const minutes = Math.floor(e.currentPosition / 60000);
42
- const seconds = Math.floor(e.currentPosition % 60000 / 1000);
43
- setTimer(`${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`);
44
- });
45
- setIsRecording(true);
46
- } catch (error) {
47
- console.error('Error starting recording:', error);
48
- }
49
- };
50
- const stopRecording = async () => {
51
- try {
52
- await audioRecorderPlayer.stopRecorder();
53
- audioRecorderPlayer.removeRecordBackListener();
54
- setIsRecording(false);
55
- } catch (error) {
56
- console.error('Error stopping recording:', error);
57
- }
58
- };
59
- const sendAudio = () => {
60
- if (audioPath) {
61
- console.log('Audio sent:', audioPath);
62
- handleSendClick(audioPath);
63
- resetState();
64
- }
65
- };
66
- const resetState = () => {
67
- setAudioPath(null);
68
- setTimer('00:00');
69
- setIsRecording(false);
70
- };
71
- return isRecording ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_StyledInputComponents.RecordContainer, {
72
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
73
- style: {
74
- display: 'flex',
75
- alignItems: 'center'
76
- },
77
- children: [isRecording && /*#__PURE__*/(0, _jsxRuntime.jsx)(_RecordingIndicator.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_StyledInputComponents.Timer, {
78
- children: timer
79
- })]
80
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
81
- style: {
82
- display: 'flex',
83
- gap: 8
84
- },
85
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
86
- onPress: stopRecording,
87
- EndIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.RemoveIcon, {}),
88
- unstyled: true
89
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
90
- onPress: sendAudio,
91
- EndIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.SendIcon, {}),
92
- unstyled: true
93
- })]
94
- })]
95
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
96
- onPress: startRecording,
97
- EndIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.RecordIcon, {})
98
- });
99
- };
100
- var _default = exports.default = AudioRecorder;
101
- //# sourceMappingURL=AudioRecorder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_react","_interopRequireWildcard","require","_StyledInputComponents","_icons","_Button","_interopRequireDefault","_RecordingIndicator","_reactNative","_reactNativeAudioRecorderPlayer","_reactNativeFs","_jsxRuntime","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","AudioRecorder","setIsRecording","isRecording","handleSendClick","audioRecorderPlayer","useRef","AudioRecorderPlayer","current","timer","setTimer","useState","audioPath","setAudioPath","useEffect","stopRecorder","removeRecordBackListener","startRecording","path","Platform","select","ios","RNFS","DocumentDirectoryPath","android","CachesDirectoryPath","uri","startRecorder","addRecordBackListener","minutes","Math","floor","currentPosition","seconds","toString","padStart","error","console","stopRecording","sendAudio","log","resetState","jsxs","RecordContainer","children","View","style","display","alignItems","jsx","Timer","gap","onPress","EndIcon","RemoveIcon","unstyled","SendIcon","RecordIcon","_default","exports"],"sourceRoot":"../../../../src","sources":["components/InputComponents/AudioRecorder.tsx"],"mappings":"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","ignoreList":[]}