@ethora/chat-component-rn 26.4.1

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 (273) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +148 -0
  3. package/empty-shim.js +15 -0
  4. package/index.js +13 -0
  5. package/metro.js +72 -0
  6. package/package.json +99 -0
  7. package/src/api.config.ts +34 -0
  8. package/src/assets/BG.png +0 -0
  9. package/src/assets/BG.tsx +56 -0
  10. package/src/assets/File.svg +4 -0
  11. package/src/assets/ForzaCare.png +0 -0
  12. package/src/assets/ForzaCare.tsx +27 -0
  13. package/src/assets/icons.tsx +685 -0
  14. package/src/assets/images.tsx +41 -0
  15. package/src/assets/test.xml +243 -0
  16. package/src/components/AuthForms/Login.tsx +295 -0
  17. package/src/components/AuthForms/LoginAnonym.tsx +68 -0
  18. package/src/components/AuthForms/Register.tsx +8 -0
  19. package/src/components/ContextMenu/ContextMenuComponents.tsx +87 -0
  20. package/src/components/DropdownMenu/DropdownMenu.tsx +244 -0
  21. package/src/components/ExampleComponents/CustomMessage.tsx +92 -0
  22. package/src/components/ExampleComponents/CustomTypingIndicatorExamples.tsx +239 -0
  23. package/src/components/Header/HeaderRoomList.tsx +104 -0
  24. package/src/components/InputComponents/AudioInput.tsx +24 -0
  25. package/src/components/InputComponents/AudioRecorder.tsx +100 -0
  26. package/src/components/InputComponents/MediaInput.tsx +114 -0
  27. package/src/components/InputComponents/RecordingIndicator.tsx +77 -0
  28. package/src/components/InputComponents/Search.tsx +135 -0
  29. package/src/components/InputComponents/TextInput.tsx +66 -0
  30. package/src/components/MainComponents/ChatHeader.tsx +163 -0
  31. package/src/components/MainComponents/ChatRoom.tsx +392 -0
  32. package/src/components/MainComponents/ChatWrapper.tsx +318 -0
  33. package/src/components/MainComponents/ChooseChatMessage.tsx +44 -0
  34. package/src/components/MainComponents/ConnectionBanner.tsx +33 -0
  35. package/src/components/MainComponents/EditWrapper.tsx +72 -0
  36. package/src/components/MainComponents/LoginWrapper.tsx +134 -0
  37. package/src/components/MainComponents/MediaMessage.tsx +55 -0
  38. package/src/components/MainComponents/MessageContainer.tsx +122 -0
  39. package/src/components/MainComponents/MessageList.tsx +429 -0
  40. package/src/components/MainComponents/NoMessagesPlaceholder.tsx +49 -0
  41. package/src/components/MainComponents/ProfileImagePlaceholder.tsx +143 -0
  42. package/src/components/MainComponents/RNStyled.tsx +34 -0
  43. package/src/components/MainComponents/ReduxWrapper.tsx +40 -0
  44. package/src/components/MainComponents/RoomList.tsx +246 -0
  45. package/src/components/MainComponents/Select.tsx +165 -0
  46. package/src/components/MainComponents/Switch.tsx +73 -0
  47. package/src/components/MainComponents/SystemMessage.tsx +42 -0
  48. package/src/components/Menu/HeaderRoomListMenu.tsx +169 -0
  49. package/src/components/MenuRoom/MenuRoom.tsx +46 -0
  50. package/src/components/MessageBubble/Avatar.tsx +84 -0
  51. package/src/components/MessageBubble/BottomReplyContainer.tsx +117 -0
  52. package/src/components/MessageBubble/CustomDivider.tsx +10 -0
  53. package/src/components/MessageBubble/DeletedMessage.tsx +36 -0
  54. package/src/components/MessageBubble/Message.tsx +400 -0
  55. package/src/components/MessageBubble/MessageInteractions.tsx +247 -0
  56. package/src/components/MessageBubble/MessageReaction.tsx +149 -0
  57. package/src/components/MessageBubble/MessageReply.tsx +50 -0
  58. package/src/components/MessageBubble/MessageTranslations.tsx +30 -0
  59. package/src/components/Modals/AddMembersModal/AddMembersModal.tsx +142 -0
  60. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +471 -0
  61. package/src/components/Modals/ChatProfileModal/DeleteChatModal.tsx +91 -0
  62. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +298 -0
  63. package/src/components/Modals/Modal/Modal.tsx +52 -0
  64. package/src/components/Modals/ModalHeaderComponent.tsx +41 -0
  65. package/src/components/Modals/ModalSelectMedia/ModalSelectMedia.tsx +285 -0
  66. package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +103 -0
  67. package/src/components/Modals/NewChatModal/ModalContent.tsx +151 -0
  68. package/src/components/Modals/NewChatModal/NewChatModal.tsx +391 -0
  69. package/src/components/Modals/NewChatModal/UpdNewChatModal.tsx +136 -0
  70. package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +134 -0
  71. package/src/components/Modals/SettingsModals/BlockedUsers/BlockedUsersModal.tsx +63 -0
  72. package/src/components/Modals/SettingsModals/DocumentShares/DocumentSharesModal.tsx +115 -0
  73. package/src/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.tsx +121 -0
  74. package/src/components/Modals/SettingsModals/ManageDataModal/StyledComponents.ts +0 -0
  75. package/src/components/Modals/SettingsModals/ProfileShares/ProfileShares.tsx +65 -0
  76. package/src/components/Modals/SettingsModals/Referrals/Referrals.tsx +90 -0
  77. package/src/components/Modals/SettingsModals/SharedStyledComponents.ts +74 -0
  78. package/src/components/Modals/SettingsModals/Visibility/RadioInput.tsx +59 -0
  79. package/src/components/Modals/SettingsModals/Visibility/StyledComponents.ts +32 -0
  80. package/src/components/Modals/SettingsModals/Visibility/VisibilityModal.tsx +146 -0
  81. package/src/components/Modals/UserProfileModal/EditUserModal.tsx +163 -0
  82. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +410 -0
  83. package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +85 -0
  84. package/src/components/Modals/styledModalComponents.tsx +181 -0
  85. package/src/components/OperationalModal/OperationalModal.tsx +78 -0
  86. package/src/components/RoomComponents/ChatRoomItem.tsx +219 -0
  87. package/src/components/RoomComponents/LastMessageItem.tsx +47 -0
  88. package/src/components/RoomComponents/styled/LastAudioMessage.tsx +62 -0
  89. package/src/components/RoomComponents/styled/LastMessageEmoji.tsx +26 -0
  90. package/src/components/RoomComponents/styled/LastMessageFile.tsx +54 -0
  91. package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +54 -0
  92. package/src/components/RoomComponents/styled/LastMessageVideo.tsx +70 -0
  93. package/src/components/RoomComponents/styled/LastTextMessage.tsx +65 -0
  94. package/src/components/RoomComponents/styled/StyledRoomComponents.ts +53 -0
  95. package/src/components/SortDropDown/index.tsx +76 -0
  96. package/src/components/Thread/ThreadHeader.tsx +41 -0
  97. package/src/components/Thread/ThreadWrapper.tsx +270 -0
  98. package/src/components/Toast/Toast.tsx +128 -0
  99. package/src/components/Toast/index.ts +13 -0
  100. package/src/components/UsersList/StyledComponents.ts +30 -0
  101. package/src/components/UsersList/UsersList.tsx +112 -0
  102. package/src/components/styled/ActionButton.tsx +40 -0
  103. package/src/components/styled/AudioMessage.tsx +82 -0
  104. package/src/components/styled/Button.tsx +107 -0
  105. package/src/components/styled/ChatWrapperBox.tsx +9 -0
  106. package/src/components/styled/DateLabel.tsx +77 -0
  107. package/src/components/styled/Loader.tsx +65 -0
  108. package/src/components/styled/MediaFilePreview.tsx +42 -0
  109. package/src/components/styled/MediaModal.tsx +54 -0
  110. package/src/components/styled/MessageImage.tsx +54 -0
  111. package/src/components/styled/NewMessageLabel.tsx +50 -0
  112. package/src/components/styled/RoomListComponents/index.tsx +164 -0
  113. package/src/components/styled/SendInput.tsx +297 -0
  114. package/src/components/styled/StyledComponents.ts +320 -0
  115. package/src/components/styled/StyledInput.tsx +101 -0
  116. package/src/components/styled/StyledInputComponents/AudioComponents.tsx +1 -0
  117. package/src/components/styled/StyledInputComponents/Composing.tsx +97 -0
  118. package/src/components/styled/StyledInputComponents/CustomTypingIndicator.tsx +261 -0
  119. package/src/components/styled/StyledInputComponents/MediaComponents.tsx +90 -0
  120. package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +131 -0
  121. package/src/components/styled/StyledInputComponents/VideoComponents.tsx +49 -0
  122. package/src/components/styled/TreadLabel.tsx +50 -0
  123. package/src/components/styled/UnsupportedType.tsx +95 -0
  124. package/src/components/styled/VideoMessage.tsx +68 -0
  125. package/src/context/MessageNotificationContext.tsx +335 -0
  126. package/src/context/PushNotificationProvider.tsx +78 -0
  127. package/src/context/ToastContext.tsx +63 -0
  128. package/src/context/toastProvider.tsx +0 -0
  129. package/src/context/xmppProvider.tsx +364 -0
  130. package/src/helpers/block_scroll.ts +95 -0
  131. package/src/helpers/chatAutoEnterer.ts +46 -0
  132. package/src/helpers/checkUniqueUsers.tsx +31 -0
  133. package/src/helpers/constants/CHAT_TYPES.ts +6 -0
  134. package/src/helpers/constants/LANGUAGE_OPTIONS.ts +14 -0
  135. package/src/helpers/constants/LOCAL_STORAGE.ts +3 -0
  136. package/src/helpers/constants/MESSAGE_INTERACTIONS.ts +29 -0
  137. package/src/helpers/constants/MODAL_TYPES.ts +57 -0
  138. package/src/helpers/constants/PLATFORM_CONSTANTS.ts +3 -0
  139. package/src/helpers/createMainMessageForThread.ts +23 -0
  140. package/src/helpers/createMessageFromXml.ts +118 -0
  141. package/src/helpers/createRoomFromApi.tsx +27 -0
  142. package/src/helpers/createUserNameFromSetUser.ts +15 -0
  143. package/src/helpers/dateComparison.ts +12 -0
  144. package/src/helpers/debounce.ts +10 -0
  145. package/src/helpers/ensureScopedChatCache.ts +61 -0
  146. package/src/helpers/extractUniqueMembersFromRooms.ts +45 -0
  147. package/src/helpers/formatNumberWithCommas.ts +3 -0
  148. package/src/helpers/getBooleanFromString.ts +7 -0
  149. package/src/helpers/getDataFromXml.ts +80 -0
  150. package/src/helpers/getNumberFromString.ts +5 -0
  151. package/src/helpers/getRoomsWithRetry.ts +45 -0
  152. package/src/helpers/getTintedColor.ts +28 -0
  153. package/src/helpers/getUnnamedUsers.ts +39 -0
  154. package/src/helpers/handleCopyClick.ts +3 -0
  155. package/src/helpers/handleXmppError.ts +14 -0
  156. package/src/helpers/hashcolor.ts +34 -0
  157. package/src/helpers/historyPreloadScheduler.ts +264 -0
  158. package/src/helpers/initRoomsPresence.ts +32 -0
  159. package/src/helpers/initXmppRooms.tsx +71 -0
  160. package/src/helpers/insertMessageWithDelimiter.ts +96 -0
  161. package/src/helpers/isChatIdPresentInArray.ts +54 -0
  162. package/src/helpers/localStorageUser.ts +9 -0
  163. package/src/helpers/markdownParser.tsx +295 -0
  164. package/src/helpers/nanoToMs.ts +3 -0
  165. package/src/helpers/parseMessageBody.tsx +516 -0
  166. package/src/helpers/parseUrls.tsx +42 -0
  167. package/src/helpers/resolveInitBeforeLoadUser.ts +240 -0
  168. package/src/helpers/storeConsole.ts +26 -0
  169. package/src/helpers/transformTranslatations.ts +16 -0
  170. package/src/helpers/updateMessagesTillLast.tsx +104 -0
  171. package/src/helpers/updatedChatLastTimestamps.ts +29 -0
  172. package/src/helpers/validator.ts +20 -0
  173. package/src/helpers/walletUsername.ts +12 -0
  174. package/src/hooks/hooks.ts +6 -0
  175. package/src/hooks/useChatSettingState.tsx +38 -0
  176. package/src/hooks/useChatWrapperInit.ts +266 -0
  177. package/src/hooks/useComposing.tsx +59 -0
  178. package/src/hooks/useCustomTypingIndicator.tsx +193 -0
  179. package/src/hooks/useEventHandlers.tsx +81 -0
  180. package/src/hooks/useGetNewArchRoom.tsx +57 -0
  181. package/src/hooks/useHeapSender.tsx +88 -0
  182. package/src/hooks/useLocalStorage.tsx +53 -0
  183. package/src/hooks/useLogout.tsx +31 -0
  184. package/src/hooks/useMessageHeapState.tsx +11 -0
  185. package/src/hooks/useMessageLoaderQueue.tsx +90 -0
  186. package/src/hooks/useMessageQueue.ts +146 -0
  187. package/src/hooks/usePendingNotification.ts +49 -0
  188. package/src/hooks/usePrevious.tsx +15 -0
  189. package/src/hooks/usePushNotifications.ts +106 -0
  190. package/src/hooks/useQRCodeChatHandler.ts +58 -0
  191. package/src/hooks/useRoomInitialization.tsx +160 -0
  192. package/src/hooks/useRoomState.tsx +47 -0
  193. package/src/hooks/useRoomUrl.tsx +41 -0
  194. package/src/hooks/useSendMessage.tsx +306 -0
  195. package/src/hooks/useUnreadMessagesCounter.ts +55 -0
  196. package/src/hooks/useXmppInitialization.ts +148 -0
  197. package/src/main.ts +45 -0
  198. package/src/networking/api-requests/auth.api.ts +140 -0
  199. package/src/networking/api-requests/push.api.ts +55 -0
  200. package/src/networking/api-requests/roomMembers.api.ts +20 -0
  201. package/src/networking/api-requests/rooms.api.ts +142 -0
  202. package/src/networking/api-requests/user.api.ts +65 -0
  203. package/src/networking/apiClient.ts +149 -0
  204. package/src/networking/helpers/refreshChecker.ts +13 -0
  205. package/src/networking/stanzaHandlers.ts +418 -0
  206. package/src/networking/xmpp/actionSetTimestampToPrivateStore.xmpp.ts +37 -0
  207. package/src/networking/xmpp/allRoomPresences.xmpp.ts +10 -0
  208. package/src/networking/xmpp/createPrivateRoom.xmpp.ts +26 -0
  209. package/src/networking/xmpp/createRoom.xmpp.ts +35 -0
  210. package/src/networking/xmpp/createRoomPresence.xmpp.ts +74 -0
  211. package/src/networking/xmpp/createTimeoutPromise.xmpp.ts +15 -0
  212. package/src/networking/xmpp/deleteMessage.xmpp.ts +19 -0
  213. package/src/networking/xmpp/editMessage.xmpp.ts +46 -0
  214. package/src/networking/xmpp/getChatsPrivateStoreRequest.xmpp.ts +57 -0
  215. package/src/networking/xmpp/getHistory.xmpp.ts +140 -0
  216. package/src/networking/xmpp/getLastMessageArchive.xmpp.ts +24 -0
  217. package/src/networking/xmpp/getRoomInfo.xmpp.ts +15 -0
  218. package/src/networking/xmpp/getRoomMembers.xmpp.ts +13 -0
  219. package/src/networking/xmpp/getRooms.xmpp.ts +43 -0
  220. package/src/networking/xmpp/getRoomsPaged.xmpp.ts +59 -0
  221. package/src/networking/xmpp/handlePong.xmpp.ts +7 -0
  222. package/src/networking/xmpp/handleStanzas.xmpp.ts +55 -0
  223. package/src/networking/xmpp/inviteRoomRequest.xmpp.ts +25 -0
  224. package/src/networking/xmpp/leaveTheRoom.xmpp.ts +15 -0
  225. package/src/networking/xmpp/listenerManager.ts +116 -0
  226. package/src/networking/xmpp/presenceInRoom.xmpp.ts +57 -0
  227. package/src/networking/xmpp/roomConfig.xmpp.ts +71 -0
  228. package/src/networking/xmpp/sendMediaMessage.xmpp.ts +97 -0
  229. package/src/networking/xmpp/sendMessageReaction.xmpp.ts +41 -0
  230. package/src/networking/xmpp/sendPing.xmpp.ts +21 -0
  231. package/src/networking/xmpp/sendTextMessage.xmpp.ts +99 -0
  232. package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +44 -0
  233. package/src/networking/xmpp/sendTypingRequest.xmpp.ts +26 -0
  234. package/src/networking/xmpp/setChatsPrivateStoreRequest.xmpp.ts +48 -0
  235. package/src/networking/xmpp/setMeAsOwner.xmpp.ts +52 -0
  236. package/src/networking/xmpp/setRoomImage.xmpp.ts +26 -0
  237. package/src/networking/xmpp/setVCard.xmpp.ts +19 -0
  238. package/src/networking/xmpp/subscribeToRoomMessages.xmpp.ts +75 -0
  239. package/src/networking/xmppClient.ts +603 -0
  240. package/src/roomStore/Middleware/logoutMiddleware.tsx +24 -0
  241. package/src/roomStore/Middleware/newMessageMidlleware.tsx +87 -0
  242. package/src/roomStore/Middleware/reactionsMiddleware.tsx +77 -0
  243. package/src/roomStore/Middleware/testMiddleware.tsx +20 -0
  244. package/src/roomStore/Middleware/unreadMidlleware.tsx +58 -0
  245. package/src/roomStore/chatSettingsSlice.ts +178 -0
  246. package/src/roomStore/index.ts +68 -0
  247. package/src/roomStore/persistence.ts +152 -0
  248. package/src/roomStore/roomHeapSlice.ts +37 -0
  249. package/src/roomStore/roomsSlice.ts +353 -0
  250. package/src/services/pushNotifications.ts +170 -0
  251. package/src/services/pushSubscriptionService.ts +145 -0
  252. package/src/types/messages.ts +61 -0
  253. package/src/types/models/action.model.ts +30 -0
  254. package/src/types/models/config.model.ts +16 -0
  255. package/src/types/models/customComponents.model.ts +53 -0
  256. package/src/types/models/language.model.ts +11 -0
  257. package/src/types/models/media.model.ts +5 -0
  258. package/src/types/models/message.model.ts +69 -0
  259. package/src/types/models/modal.model.ts +9 -0
  260. package/src/types/models/room.model.ts +120 -0
  261. package/src/types/models/user.model.ts +90 -0
  262. package/src/types/models/xmpp.model.ts +155 -0
  263. package/src/types/types.ts +505 -0
  264. package/src/utils/PriorityQueue.ts +47 -0
  265. package/src/utils/actionHelpers.ts +40 -0
  266. package/src/utils/actionLogger.ts +64 -0
  267. package/src/utils/clientRegistry.ts +80 -0
  268. package/src/utils/devLogger.ts +197 -0
  269. package/src/utils/messageNotificationManager.ts +108 -0
  270. package/src/utils/resendMessage.ts +87 -0
  271. package/src/utils/toastEmitter.ts +27 -0
  272. package/src/utils/xmppMemoryMonitor.ts +63 -0
  273. package/src/version.ts +1 -0
@@ -0,0 +1,118 @@
1
+ import { IMessage } from '../types/types';
2
+
3
+ interface IMessageWithNewData extends IMessage {
4
+ [x: string]: any;
5
+ }
6
+
7
+ /**
8
+ * Build an IMessage from XML.
9
+ *
10
+ * Two calling conventions are supported:
11
+ *
12
+ * 1. **Wrapped (used by getHistory.xmpp.ts)**:
13
+ * createMessageFromXml({ data, id, body, roomJid, date, user, ...rest })
14
+ * where `data` is the inner XML `<data>` element's attrs.
15
+ *
16
+ * 2. **Positional (used by onRealtimeMessage / onMessageHistory)**:
17
+ * createMessageFromXml(dataAttrs, bodyElement, id, fromJid, deleted)
18
+ *
19
+ * Both fan into the same merge logic that flattens senderFirstName /
20
+ * senderLastName / senderJID into `user.{name, firstName, lastName, id}`.
21
+ */
22
+ export const createMessageFromXml = async (
23
+ arg: IMessageWithNewData,
24
+ bodyEl?: any,
25
+ positionalId?: string,
26
+ positionalFrom?: string,
27
+ positionalDeleted?: boolean
28
+ ): Promise<IMessage> => {
29
+ if (!arg) {
30
+ console.log('createMessageFromXml: arg is required.');
31
+ // Return a stub so callers don't crash; the reducer will discard
32
+ // messages without a usable id/date anyway.
33
+ return { id: '', body: '', date: '', user: { id: '', name: '' } as any, roomJid: '' } as IMessage;
34
+ }
35
+
36
+ // Detect positional vs wrapped. Wrapped always has a top-level
37
+ // `data` key holding the flat attrs; positional passes the attrs
38
+ // directly so `arg.data` is undefined.
39
+ const isPositional = bodyEl !== undefined || positionalId !== undefined;
40
+
41
+ let inner: any; // flat XML <data> attrs
42
+ let outerExtras: any = {};
43
+
44
+ if (isPositional) {
45
+ inner = arg; // arg IS the dataAttrs in this convention
46
+ const bodyText =
47
+ typeof bodyEl === 'string'
48
+ ? bodyEl
49
+ : bodyEl?.getText
50
+ ? bodyEl.getText()
51
+ : bodyEl?.text;
52
+ outerExtras = {
53
+ id: positionalId,
54
+ body: bodyText,
55
+ roomJid: (positionalFrom || '').split('/')[0],
56
+ xmppFrom: positionalFrom,
57
+ deleted: !!positionalDeleted,
58
+ };
59
+ } else {
60
+ inner = arg?.data || {};
61
+ outerExtras = { ...arg };
62
+ delete outerExtras.data;
63
+ }
64
+
65
+ const merged: any = {
66
+ ...inner,
67
+ ...outerExtras,
68
+ isDeleted: !!outerExtras?.deleted || !!(arg as any)?.isDeleted,
69
+ };
70
+
71
+ // Date fallback: when called via the realtime path, the stanza may
72
+ // not carry a date attr — derive it from the message id (server
73
+ // timestamps are encoded in the stanza-id microseconds prefix), and
74
+ // ultimately fall back to "now" so the reducer's date-based ordering
75
+ // doesn't crash on `undefined.toString()`.
76
+ if (!merged.date) {
77
+ const numericPart = /\d{13,}/.exec(String(merged.id || ''))?.[0];
78
+ merged.date = numericPart
79
+ ? new Date(+numericPart.slice(0, 13)).toISOString()
80
+ : new Date().toISOString();
81
+ }
82
+ if (!merged.id) {
83
+ merged.id = String(Date.now());
84
+ }
85
+
86
+ // Synthesize user.name from sender attrs (web reads usersSet from
87
+ // redux in <Message>; we fix it once at the source so Avatar /
88
+ // MessageHeader render the actual sender instead of "??").
89
+ const firstName = (merged.senderFirstName || '').toString().trim();
90
+ const lastName = (merged.senderLastName || '').toString().trim();
91
+ const fullName = (merged.fullName || '').toString().trim();
92
+ const senderJID = (merged.senderJID || '').toString();
93
+ const senderLocal = senderJID.includes('@')
94
+ ? senderJID.split('@')[0]
95
+ : senderJID;
96
+ const composed = [firstName, lastName].filter(Boolean).join(' ').trim();
97
+ const existingName = String(merged?.user?.name || '').trim();
98
+ const resolvedName =
99
+ existingName ||
100
+ composed ||
101
+ fullName ||
102
+ senderLocal ||
103
+ merged?.user?.id ||
104
+ '';
105
+
106
+ const message: IMessage = {
107
+ ...merged,
108
+ user: {
109
+ ...(merged.user || {}),
110
+ id: merged?.user?.id || senderLocal,
111
+ name: resolvedName,
112
+ firstName: firstName || merged?.user?.firstName,
113
+ lastName: lastName || merged?.user?.lastName,
114
+ },
115
+ };
116
+
117
+ return message;
118
+ };
@@ -0,0 +1,27 @@
1
+ import { ApiRoom, IRoom } from '../types/types';
2
+
3
+ export const createRoomFromApi = (
4
+ room: ApiRoom,
5
+ service: string = 'conference.dev.xmpp.ethoradev.com',
6
+ usersArrayLength: number = 0
7
+ ): IRoom | null => {
8
+ try {
9
+ const roomData: IRoom = {
10
+ ...room,
11
+ jid: `${room?.name}@${service}` || '',
12
+ name: room?.title || '',
13
+ title: room?.title || '',
14
+ usersCnt: Number(room?.members?.length || usersArrayLength + 1),
15
+ messages: [],
16
+ isLoading: false,
17
+ roomBg: null,
18
+ icon: room?.picture !== 'none' ? room?.picture : null,
19
+ unreadMessages: 0,
20
+ lastViewedTimestamp: 0,
21
+ };
22
+ return roomData;
23
+ } catch (error) {
24
+ console.log(error);
25
+ return null;
26
+ }
27
+ };
@@ -0,0 +1,15 @@
1
+ import { RoomMember } from '../types/types';
2
+
3
+ export const createUserNameFromSetUser = (
4
+ usersSet: Record<string, RoomMember>,
5
+ userId: string
6
+ ): string => {
7
+ const user = usersSet[userId];
8
+
9
+ if (!user) {return 'Deleted User';}
10
+
11
+ const firstName = user.firstName?.trim() || '';
12
+ const lastName = user.lastName?.trim() || '';
13
+
14
+ return `${firstName} ${lastName}`.trim() || userId;
15
+ };
@@ -0,0 +1,12 @@
1
+ export const isDateBefore = (date1: string, date2: string): boolean => {
2
+ return new Date(date1) < new Date(date2);
3
+ };
4
+ export const isDateAfter = (date1: string, date2: string): boolean => {
5
+ return new Date(date1) > new Date(date2);
6
+ };
7
+
8
+ export const getHighResolutionTimestamp = () => {
9
+ const milliseconds = Date.now();
10
+ const microseconds = Math.floor(performance.now() * 10);
11
+ return `${milliseconds}${microseconds.toString().padStart(6, '0')}`;
12
+ };
@@ -0,0 +1,10 @@
1
+ export function debounce<T extends (...args: any[]) => void>(
2
+ func: T,
3
+ delay: number
4
+ ): (...args: Parameters<T>) => void {
5
+ let timeoutId: ReturnType<typeof setTimeout>;
6
+ return (...args: Parameters<T>) => {
7
+ clearTimeout(timeoutId);
8
+ timeoutId = setTimeout(() => func(...args), delay);
9
+ };
10
+ }
@@ -0,0 +1,61 @@
1
+ import { IConfig } from '../types/types';
2
+ import { store } from '../roomStore';
3
+ import { logout } from '../roomStore/chatSettingsSlice';
4
+ import { setLogoutState } from '../roomStore/roomsSlice';
5
+ import { asyncLocalStorage } from '../hooks/useLocalStorage';
6
+ import { localStorageConstants } from './constants/LOCAL_STORAGE';
7
+ import { clearRoomsRestCache } from '../networking/api-requests/rooms.api';
8
+ import { clearPersistedState } from '../roomStore/persistence';
9
+ import {
10
+ getGlobalXmppClient,
11
+ setGlobalXmppClient,
12
+ } from '../utils/clientRegistry';
13
+
14
+ const SCOPE_KEY = '@ethora/chat-component-scope';
15
+
16
+ interface ScopeRecord {
17
+ appId?: string;
18
+ baseUrl?: string;
19
+ }
20
+
21
+ /**
22
+ * Detect an app-scope change (different appId / baseUrl). When detected,
23
+ * purge the persisted user, redux user/client/rooms state, REST caches,
24
+ * and the global xmpp client singleton so the next bootstrap re-resolves
25
+ * against the new tenant. Mirrors web ensureScopedChatCache.
26
+ */
27
+ export async function ensureScopedChatCache(config?: IConfig): Promise<void> {
28
+ if (!config?.appId && !config?.baseUrl) {return;}
29
+
30
+ const next: ScopeRecord = {
31
+ appId: config.appId,
32
+ baseUrl: config.baseUrl,
33
+ };
34
+
35
+ const scopeStore = asyncLocalStorage<ScopeRecord>(SCOPE_KEY);
36
+ const previous = await scopeStore.get();
37
+
38
+ const changed =
39
+ previous &&
40
+ (previous.appId !== next.appId || previous.baseUrl !== next.baseUrl);
41
+
42
+ if (changed) {
43
+ try {
44
+ const client = getGlobalXmppClient();
45
+ if (client) {
46
+ await client.disconnect?.({ suppressReconnect: true });
47
+ }
48
+ } catch {
49
+ /* noop */
50
+ }
51
+ setGlobalXmppClient(null);
52
+
53
+ clearRoomsRestCache();
54
+ store.dispatch(setLogoutState());
55
+ store.dispatch(logout());
56
+ await asyncLocalStorage(localStorageConstants.ETHORA_USER).remove();
57
+ await clearPersistedState();
58
+ }
59
+
60
+ await scopeStore.set(next);
61
+ }
@@ -0,0 +1,45 @@
1
+ import { RoomMember, ApiRoom } from '../types/types';
2
+
3
+ interface MemberExtractionResult {
4
+ array: RoomMember[];
5
+ object: Record<string, RoomMember>;
6
+ map: Map<string, RoomMember>;
7
+ set: Set<string>;
8
+ }
9
+
10
+ export function extractUniqueMembersFromRooms(
11
+ rooms: ApiRoom[]
12
+ ): MemberExtractionResult {
13
+ const uniqueMembersMap = new Map<string, RoomMember>();
14
+
15
+ rooms.forEach((room) => {
16
+ if (room.members) {
17
+ room.members.forEach((member) => {
18
+ if (member && member?.xmppUsername) {
19
+ uniqueMembersMap.set(member.xmppUsername, member);
20
+ }
21
+ });
22
+ }
23
+ });
24
+
25
+ const uniqueMembersArray = Array.from(uniqueMembersMap.values());
26
+
27
+ const uniqueMembersObject = uniqueMembersArray.reduce(
28
+ (acc, member) => {
29
+ if (member.xmppUsername) {
30
+ acc[member.xmppUsername] = member;
31
+ }
32
+ return acc;
33
+ },
34
+ {} as Record<string, RoomMember>
35
+ );
36
+
37
+ const uniqueMembersSet = new Set(uniqueMembersMap.keys());
38
+
39
+ return {
40
+ array: uniqueMembersArray,
41
+ object: uniqueMembersObject,
42
+ map: uniqueMembersMap,
43
+ set: uniqueMembersSet,
44
+ };
45
+ }
@@ -0,0 +1,3 @@
1
+ export function formatNumberWithCommas(num: number | string): string {
2
+ return num.toLocaleString('en-US');
3
+ }
@@ -0,0 +1,7 @@
1
+ export function getBooleanFromString(value: string): boolean | null {
2
+ if (!value) {return null;}
3
+ const normalized = value.trim().toLowerCase();
4
+ if (normalized === 'true') {return true;}
5
+ if (normalized === 'false') {return false;}
6
+ return null;
7
+ }
@@ -0,0 +1,80 @@
1
+ import { Element } from 'ltx';
2
+ import { Iso639_1Codes, IUser } from '../types/types';
3
+ import { transformArrayToObject } from './transformTranslatations';
4
+
5
+ const extractTimestamp = (str: string, stanza?: any): string | null => {
6
+ if (!str) {return null;}
7
+ if (typeof str !== 'string') {
8
+ console.log(str, stanza.toString());
9
+ return null;
10
+ }
11
+ const timestamp = str.slice(-16);
12
+ return timestamp;
13
+ };
14
+
15
+ interface DataXml {
16
+ id: string;
17
+ body?: string;
18
+ roomJid: string;
19
+ date: string;
20
+ user: IUser;
21
+ deleted?: boolean;
22
+ translations?: any;
23
+ langSource?: Iso639_1Codes;
24
+ xmppId?: string;
25
+ xmppFrom?: string;
26
+ data: { [x: string]: any };
27
+ }
28
+ export const getDataFromXml = async (stanza: Element): Promise<DataXml | undefined> => {
29
+ const fullData =
30
+ stanza.getChild('result')?.getChild('forwarded')?.getChild('message') ||
31
+ stanza;
32
+
33
+ const xmppId = fullData?.attrs.id;
34
+ const xmppFrom = fullData?.attrs?.from;
35
+ const [roomJid, userWallet] = xmppFrom.split('/');
36
+ let id =
37
+ stanza.getChild('result')?.attrs.id ||
38
+ extractTimestamp(stanza?.getChild('stanza-id')?.attrs?.id, stanza);
39
+
40
+ if (!id) {
41
+ id = xmppId || Date.now().toString();
42
+ }
43
+
44
+ const body = fullData?.getChild('body')?.getText() || undefined;
45
+ const deleted = !!fullData?.getChild('deleted');
46
+ const translations = fullData?.getChild('translations')?.attrs?.value
47
+ ? transformArrayToObject(
48
+ JSON.parse(fullData.getChild('translations')!.attrs.value).translates
49
+ )
50
+ : undefined;
51
+ const langSource = fullData?.getChild('translate')?.attrs?.source;
52
+ const numericPart = /\d{13,}/.exec(id || '')?.[0];
53
+ const date = numericPart
54
+ ? new Date(+numericPart.slice(0, 13)).toISOString()
55
+ : new Date().toISOString();
56
+
57
+ const data = fullData?.getChild('data') || stanza?.getChild('data');
58
+ const photoURL = data?.attrs?.photo;
59
+
60
+ const user = {
61
+ id: userWallet,
62
+ photoURL,
63
+ };
64
+
65
+ const dataAttrs = data?.attrs || {};
66
+
67
+ return {
68
+ data: dataAttrs,
69
+ id,
70
+ body,
71
+ roomJid,
72
+ date,
73
+ user,
74
+ deleted,
75
+ translations,
76
+ langSource,
77
+ xmppId,
78
+ xmppFrom,
79
+ };
80
+ };
@@ -0,0 +1,5 @@
1
+ export function getNumberFromString(value: string): number | null {
2
+ if (!value) {return null;}
3
+ const num = Number(value.trim());
4
+ return isNaN(num) ? null : num;
5
+ }
@@ -0,0 +1,45 @@
1
+ import { ApiRoom } from '../types/types';
2
+ import { isChatIdPresentInArray } from './isChatIdPresentInArray';
3
+
4
+ export async function getRoomsWithRetry(
5
+ client: any,
6
+ config: any,
7
+ syncRoomsFunction: (arg0: any, arg1: any) => any[] | PromiseLike<any[]>,
8
+ activeChatJid?: string | null,
9
+ maxRetries = 10,
10
+ delayBetweenRetriesMs = 7500
11
+ ): Promise<ApiRoom[] | null> {
12
+ let attempts = 0;
13
+ let rooms = [];
14
+
15
+ while (attempts <= maxRetries) {
16
+ try {
17
+ rooms = await syncRoomsFunction(client, config);
18
+
19
+ if (activeChatJid && Array.isArray(rooms)) {
20
+ const isSelectedRoomPresent = isChatIdPresentInArray(
21
+ activeChatJid,
22
+ rooms
23
+ );
24
+ if (isSelectedRoomPresent) {
25
+ return rooms;
26
+ }
27
+ } else if (Array.isArray(rooms) && rooms.length > 0) {
28
+ return rooms;
29
+ }
30
+ } catch (error) {
31
+ console.error(`Error during attempt ${attempts + 1}:`, error);
32
+ }
33
+
34
+ attempts++;
35
+ if (attempts <= maxRetries) {
36
+ await delay(delayBetweenRetriesMs);
37
+ }
38
+ }
39
+
40
+ return null;
41
+ }
42
+
43
+ function delay(ms: number) {
44
+ return new Promise((resolve) => setTimeout(resolve, ms));
45
+ }
@@ -0,0 +1,28 @@
1
+ export const getTintedColor = (hex: string, amount: number = 0.2) => {
2
+ if (hex.startsWith('#')) {
3
+ hex = hex.slice(1);
4
+ }
5
+
6
+ if (hex.length === 3) {
7
+ hex = hex
8
+ .split('')
9
+ .map((x) => x + x)
10
+ .join('');
11
+ }
12
+
13
+ const num = parseInt(hex, 16);
14
+ const r = (num >> 16) & 255;
15
+ const g = (num >> 8) & 255;
16
+ const b = num & 255;
17
+
18
+ const tintedR = Math.round(r + (255 - r) * amount);
19
+ const tintedG = Math.round(g + (255 - g) * amount);
20
+ const tintedB = Math.round(b + (255 - b) * amount);
21
+
22
+ const tintedHex = `#${((1 << 24) + (tintedR << 16) + (tintedG << 8) + tintedB)
23
+ .toString(16)
24
+ .slice(1)
25
+ .toUpperCase()}`;
26
+
27
+ return tintedHex;
28
+ };
@@ -0,0 +1,39 @@
1
+ import { getUserByXmppUsername } from '../networking/api-requests/roomMembers.api';
2
+ import { store } from '../roomStore';
3
+ import { IMessage, IUser, RoomMember } from '../types/types';
4
+
5
+ function getUniqueUsers(messages: IMessage[]): Set<IUser> {
6
+ const userMap = new Map<string, IUser>();
7
+
8
+ messages.forEach(({ user }) => {
9
+ if (!userMap.has(user.id)) {
10
+ userMap.set(user.id, user);
11
+ }
12
+ });
13
+
14
+ return new Set(userMap.values());
15
+ }
16
+
17
+ export const getUnnamedUsers = (messages: IMessage[]): IUser[] => {
18
+ const uniqueUsers = getUniqueUsers(messages);
19
+
20
+ return [...uniqueUsers].filter((user) =>
21
+ user?.name.toLowerCase().includes('deleted')
22
+ );
23
+ };
24
+
25
+ export const fixUnnamedArrayFromApi = async (
26
+ unnamedUsers: IUser[]
27
+ ): Promise<RoomMember[]> => {
28
+ const fetchedUsers = await Promise.all(
29
+ unnamedUsers.map(async (user) => {
30
+ const apiUser = await getUserByXmppUsername(
31
+ user.id,
32
+ store.getState().chatSettingStore.user.token
33
+ );
34
+ return apiUser;
35
+ })
36
+ );
37
+
38
+ return fetchedUsers;
39
+ };
@@ -0,0 +1,3 @@
1
+ export const handleCopyClick = (string: string) => {
2
+ navigator.clipboard.writeText(string);
3
+ };
@@ -0,0 +1,14 @@
1
+ export function findError(node: Element): {
2
+ hasError: boolean;
3
+ errorText: string | null;
4
+ } {
5
+ if (node.tagName === 'error') {
6
+ const text = node.textContent?.trim() || null;
7
+ return { hasError: true, errorText: text };
8
+ }
9
+ for (let i = 0; i < node.children.length; i++) {
10
+ const result = findError(node.children[i]);
11
+ if (result.hasError) {return result;}
12
+ }
13
+ return { hasError: false, errorText: null };
14
+ }
@@ -0,0 +1,34 @@
1
+ import { sha256 } from 'js-sha256';
2
+
3
+ export function nameToColor(name: string): {
4
+ backgroundColor: string;
5
+ } | null {
6
+ const avatarColors = [
7
+ '#86d1ee',
8
+ '#badfff',
9
+ '#E2F4FB',
10
+ '#B1E1D9',
11
+ '#B8F1C4',
12
+ '#E0FFBE',
13
+ '#D3C2F1',
14
+ '#EEE6F9',
15
+ '#E7BDE6',
16
+ '#FEBDD1',
17
+ '#FFEBEE',
18
+ '#F1DCB6',
19
+ '#FFE0B6',
20
+ '#E8E4C9',
21
+ '#F5F2BC',
22
+ ];
23
+
24
+ if (!name) {
25
+ return { backgroundColor: 'transparent' };
26
+ }
27
+
28
+ return {
29
+ backgroundColor:
30
+ avatarColors[
31
+ parseInt(sha256(name).slice(0, 2), 16) % avatarColors.length
32
+ ],
33
+ };
34
+ }