@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,364 @@
1
+ import React, {
2
+ ReactNode,
3
+ createContext,
4
+ useCallback,
5
+ useContext,
6
+ useEffect,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import { DeviceEventEmitter } from 'react-native';
11
+ import XmppClient from '../networking/xmppClient';
12
+ import {
13
+ IConfig,
14
+ ProviderBootstrapStatus,
15
+ User,
16
+ xmppSettingsInterface,
17
+ } from '../types/types';
18
+ import {
19
+ buildXmppClientKey,
20
+ getReusableXmppClientByKey,
21
+ isXmppClientReusable,
22
+ setGlobalXmppClient,
23
+ withXmppClientInitLock,
24
+ } from '../utils/clientRegistry';
25
+ import {
26
+ applyResolvedUserToStore,
27
+ resolveInitBeforeLoadUser,
28
+ } from '../helpers/resolveInitBeforeLoadUser';
29
+ import { ensureScopedChatCache } from '../helpers/ensureScopedChatCache';
30
+ import { getRooms as prefetchRoomsViaRest } from '../networking/api-requests/rooms.api';
31
+ import { store } from '../roomStore';
32
+ import { logout, setStoreClient } from '../roomStore/chatSettingsSlice';
33
+ import { setLogoutState } from '../roomStore/roomsSlice';
34
+ import { runHistoryPreloadScheduler } from '../helpers/historyPreloadScheduler';
35
+ import { asyncLocalStorage } from '../hooks/useLocalStorage';
36
+ import { localStorageConstants } from '../helpers/constants/LOCAL_STORAGE';
37
+ import { clearPersistedState } from '../roomStore/persistence';
38
+ import { pushLog as devPushLog } from '../utils/devLogger';
39
+
40
+ interface InitMode {
41
+ current: 'provider' | 'chat';
42
+ }
43
+
44
+ interface XmppContextType {
45
+ client: XmppClient | null;
46
+ providerBootstrapStatus: ProviderBootstrapStatus;
47
+ initMode: InitMode['current'];
48
+ setClient: (client: XmppClient | null) => void;
49
+ initializeClient: (
50
+ username: string,
51
+ password: string,
52
+ xmppSettings?: xmppSettingsInterface
53
+ ) => Promise<XmppClient>;
54
+ }
55
+
56
+ const XmppContext = createContext<XmppContextType | null>(null);
57
+
58
+ interface XmppProviderProps {
59
+ children: ReactNode;
60
+ config?: IConfig;
61
+ }
62
+
63
+ const LOGOUT_EVENT = 'ethora-xmpp-logout';
64
+
65
+ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config }) => {
66
+ const [client, setClient] = useState<XmppClient | null>(null);
67
+ const [providerBootstrapStatus, setProviderBootstrapStatus] =
68
+ useState<ProviderBootstrapStatus>('idle');
69
+ const [reconnectAttempts, setReconnectAttempts] = useState(0);
70
+
71
+ // Track which "init mode" this provider runs in. When config.initBeforeLoad
72
+ // is true, the provider owns bootstrap and ChatWrapper just waits.
73
+ const initMode: InitMode['current'] = config?.initBeforeLoad ? 'provider' : 'chat';
74
+
75
+ const lastCredsRef = useRef<{ username: string; password: string; settings?: xmppSettingsInterface } | null>(null);
76
+ const completedBootstrapKeyRef = useRef<string>('');
77
+ const inflightBootstrapKeyRef = useRef<string>('');
78
+
79
+ const initializeClient = useCallback(
80
+ async (
81
+ username: string,
82
+ password: string,
83
+ xmppSettings?: xmppSettingsInterface
84
+ ): Promise<XmppClient> => {
85
+ const settings = xmppSettings || config?.xmppSettings;
86
+ const key = buildXmppClientKey(username, settings);
87
+
88
+ // 1. Reuse global singleton if it's the same key.
89
+ const reusable = getReusableXmppClientByKey(key);
90
+ if (reusable) {
91
+ setClient(reusable);
92
+ lastCredsRef.current = { username, password, settings };
93
+ return reusable;
94
+ }
95
+
96
+ // 2. Reuse current provider client if still usable.
97
+ if (client && isXmppClientReusable(client)) {
98
+ lastCredsRef.current = { username, password, settings };
99
+ return client;
100
+ }
101
+
102
+ // 3. Otherwise create a new client under the init lock to dedup
103
+ // concurrent calls for the same key.
104
+ const created = await withXmppClientInitLock(key, async () => {
105
+ const newClient = new XmppClient(username, password, settings);
106
+ setGlobalXmppClient(newClient, key);
107
+ await newClient.waitForOnline().catch((err) => {
108
+ console.error('Error waiting for xmpp online:', err);
109
+ throw err;
110
+ });
111
+ return newClient;
112
+ });
113
+
114
+ setClient(created);
115
+ lastCredsRef.current = { username, password, settings };
116
+ setReconnectAttempts(0);
117
+ return created;
118
+ },
119
+ [client, config?.xmppSettings]
120
+ );
121
+
122
+ // -----------------------------------------------------------
123
+ // initBeforeLoad — eager auth + xmpp connect at provider mount
124
+ // -----------------------------------------------------------
125
+ useEffect(() => {
126
+ if (!config?.initBeforeLoad) {
127
+ setProviderBootstrapStatus('idle');
128
+ return;
129
+ }
130
+
131
+ const abortController = new AbortController();
132
+ let cancelled = false;
133
+
134
+ const run = async () => {
135
+ // Stable bootstrap key — re-run only when these change.
136
+ const explicitUser = config?.userLogin?.enabled ? config?.userLogin?.user : null;
137
+ const key = [
138
+ config?.appId || '',
139
+ config?.baseUrl || '',
140
+ explicitUser?.defaultWallet?.walletAddress || '',
141
+ explicitUser?.xmppUsername || '',
142
+ explicitUser?.token ? '1' : '0',
143
+ config?.jwtLogin?.enabled ? config?.jwtLogin?.token || '' : '',
144
+ config?.xmppSettings?.devServer || '',
145
+ ].join('|');
146
+
147
+ if (completedBootstrapKeyRef.current === key) {
148
+ devPushLog('rn', 'initBeforeLoad: already completed (same key) — skip');
149
+ return;
150
+ }
151
+ if (inflightBootstrapKeyRef.current === key) {
152
+ devPushLog('rn', 'initBeforeLoad: in-flight (same key) — skip');
153
+ return;
154
+ }
155
+ inflightBootstrapKeyRef.current = key;
156
+ setProviderBootstrapStatus('running');
157
+ devPushLog('rn', 'initBeforeLoad: running', {
158
+ appId: config?.appId,
159
+ baseUrl: config?.baseUrl,
160
+ mode:
161
+ config?.jwtLogin?.enabled ? 'jwt' :
162
+ config?.userLogin?.enabled ? 'userLogin' :
163
+ config?.customLogin?.enabled ? 'customLogin' : 'fallback',
164
+ });
165
+
166
+ try {
167
+ if (config?.clearStoreBeforeInit) {
168
+ devPushLog('rn', 'initBeforeLoad: clearStoreBeforeInit');
169
+ store.dispatch(setLogoutState());
170
+ store.dispatch(logout());
171
+ await asyncLocalStorage(localStorageConstants.ETHORA_USER).remove().catch(() => undefined);
172
+ }
173
+
174
+ await ensureScopedChatCache(config);
175
+ devPushLog('rn', 'initBeforeLoad: scope ok, resolving user…');
176
+
177
+ const resolved: User | null = await resolveInitBeforeLoadUser({
178
+ config,
179
+ signal: abortController.signal,
180
+ });
181
+ if (cancelled) {return;}
182
+
183
+ if (!resolved || !resolved.xmppPassword || !resolved.xmppUsername) {
184
+ devPushLog('error', 'initBeforeLoad: user resolve failed', {
185
+ hasResolved: !!resolved,
186
+ xmppUsername: resolved?.xmppUsername,
187
+ xmppPasswordPresent: !!resolved?.xmppPassword,
188
+ });
189
+ setProviderBootstrapStatus('failed');
190
+ inflightBootstrapKeyRef.current = '';
191
+ return;
192
+ }
193
+
194
+ devPushLog('rn', `initBeforeLoad: user resolved (${resolved.xmppUsername})`);
195
+ applyResolvedUserToStore(resolved);
196
+
197
+ prefetchRoomsViaRest().catch(() => undefined);
198
+
199
+ const c = await initializeClient(
200
+ resolved.xmppUsername!,
201
+ resolved.xmppPassword,
202
+ config?.xmppSettings
203
+ );
204
+ if (cancelled) {return;}
205
+ devPushLog('rn', 'initBeforeLoad: xmpp client created, waiting online…');
206
+
207
+ await c.waitForOnline();
208
+ if (cancelled) {return;}
209
+ devPushLog('xmpp', 'initBeforeLoad: xmpp online');
210
+
211
+ try {
212
+ await c.getRoomsStanza();
213
+ devPushLog('xmpp', 'initBeforeLoad: getRoomsStanza ok');
214
+ } catch (e) {
215
+ devPushLog('warn', 'initBeforeLoad: getRoomsStanza failed', e);
216
+ }
217
+ try {
218
+ await c.getChatsPrivateStoreRequestStanza();
219
+ devPushLog('xmpp', 'initBeforeLoad: privateStore ok');
220
+ } catch (e) {
221
+ devPushLog('warn', 'initBeforeLoad: privateStore failed', e);
222
+ }
223
+
224
+ store.dispatch(setStoreClient(c));
225
+ completedBootstrapKeyRef.current = key;
226
+ inflightBootstrapKeyRef.current = '';
227
+ setProviderBootstrapStatus('ready');
228
+ devPushLog('rn', 'initBeforeLoad: READY');
229
+
230
+ // Background-prefetch history for every room (fire-and-forget).
231
+ const qos = config?.historyQoS;
232
+ const activeRoomJID = store.getState().rooms.activeRoomJID || null;
233
+ const defaultRoomJids =
234
+ (config?.defaultRooms as any[])?.map((r) =>
235
+ typeof r === 'string' ? r : r?.jid
236
+ ).filter(Boolean) || [];
237
+ runHistoryPreloadScheduler({
238
+ client: c,
239
+ signal: abortController.signal,
240
+ selectedRoomJid: activeRoomJID,
241
+ defaultRoomJids,
242
+ concurrency: qos?.stagedPreloadConcurrency,
243
+ pageSize: qos?.stagedPreloadFirstPassSize,
244
+ roomLimit: qos?.preloadTopKRooms,
245
+ }).catch((err) => console.warn('History preload scheduler failed', err));
246
+ } catch (error: any) {
247
+ if (cancelled) {return;}
248
+ devPushLog('error', 'initBeforeLoad: bootstrap threw', {
249
+ message: error?.message,
250
+ status: error?.response?.status,
251
+ data: error?.response?.data,
252
+ });
253
+ inflightBootstrapKeyRef.current = '';
254
+ setProviderBootstrapStatus('failed');
255
+ }
256
+ };
257
+
258
+ run();
259
+
260
+ return () => {
261
+ cancelled = true;
262
+ abortController.abort();
263
+ // Re-running this effect (deps changed or unmount) must release the
264
+ // in-flight bootstrap key so the next render isn't permanently
265
+ // short-circuited by the early `inflightBootstrapKeyRef === key`
266
+ // check. Without this, a `setClient` mid-bootstrap that re-renders
267
+ // the provider strands the bootstrap in `running`.
268
+ inflightBootstrapKeyRef.current = '';
269
+ };
270
+ // NOTE: `initializeClient` is intentionally NOT in the dep array.
271
+ // It's a `useCallback` that depends on `client` state; including it
272
+ // would force the effect to re-run every time `setClient` fires
273
+ // (which happens DURING bootstrap), causing a cancel/re-init storm.
274
+ // The function is still reachable through closure; its identity drift
275
+ // is irrelevant to bootstrap semantics.
276
+ // eslint-disable-next-line react-hooks/exhaustive-deps
277
+ }, [
278
+ config?.initBeforeLoad,
279
+ config?.appId,
280
+ config?.baseUrl,
281
+ config?.userLogin?.enabled,
282
+ config?.userLogin?.user?.defaultWallet?.walletAddress,
283
+ config?.userLogin?.user?.xmppUsername,
284
+ config?.userLogin?.user?.token,
285
+ config?.jwtLogin?.enabled,
286
+ config?.jwtLogin?.token,
287
+ config?.xmppSettings?.devServer,
288
+ ]);
289
+
290
+ // -----------------------------------------------------------
291
+ // Reconnect / reinit on disconnect
292
+ // -----------------------------------------------------------
293
+ useEffect(() => {
294
+ if (!client) {return;}
295
+ if (client.status !== 'offline') {return;}
296
+
297
+ if (reconnectAttempts < 3) {
298
+ console.log(`xmpp reconnect attempt ${reconnectAttempts + 1}`);
299
+ client.scheduleReconnect();
300
+ setReconnectAttempts((prev) => prev + 1);
301
+ return;
302
+ }
303
+
304
+ // After 3 failed reconnects, drop the client and re-init from scratch.
305
+ if (lastCredsRef.current) {
306
+ const { username, password, settings } = lastCredsRef.current;
307
+ setGlobalXmppClient(null);
308
+ setClient(null);
309
+ initializeClient(username, password, settings).catch((err) =>
310
+ console.error('Full reinit failed:', err)
311
+ );
312
+ }
313
+ }, [client, client?.status, reconnectAttempts, initializeClient]);
314
+
315
+ // -----------------------------------------------------------
316
+ // Logout event listener (RN equivalent of window event)
317
+ // -----------------------------------------------------------
318
+ useEffect(() => {
319
+ const sub = DeviceEventEmitter.addListener(LOGOUT_EVENT, async () => {
320
+ try {
321
+ if (client) {
322
+ await client.disconnect({ suppressReconnect: true });
323
+ }
324
+ } catch (e) {
325
+ console.warn('Logout disconnect failed', e);
326
+ }
327
+ setGlobalXmppClient(null);
328
+ setClient(null);
329
+ completedBootstrapKeyRef.current = '';
330
+ inflightBootstrapKeyRef.current = '';
331
+ setReconnectAttempts(0);
332
+ setProviderBootstrapStatus('idle');
333
+ await clearPersistedState();
334
+ });
335
+ return () => sub.remove();
336
+ }, [client]);
337
+
338
+ return (
339
+ <XmppContext.Provider
340
+ value={{
341
+ client,
342
+ providerBootstrapStatus,
343
+ initMode,
344
+ initializeClient,
345
+ setClient,
346
+ }}
347
+ >
348
+ {children}
349
+ </XmppContext.Provider>
350
+ );
351
+ };
352
+
353
+ export const useXmppClient = () => {
354
+ const context = useContext(XmppContext);
355
+ if (!context) {
356
+ throw new Error('useXmppClient must be used within an XmppProvider');
357
+ }
358
+ return context;
359
+ };
360
+
361
+ /** Trigger a global xmpp logout — mirrors the web `ethora-xmpp-logout` event. */
362
+ export function emitXmppLogout() {
363
+ DeviceEventEmitter.emit(LOGOUT_EVENT);
364
+ }
@@ -0,0 +1,95 @@
1
+ type TouchEventCallback = (e: TouchEvent) => void;
2
+ type WheelEventCallback = (e: WheelEvent) => void;
3
+
4
+ interface ElemEvents {
5
+ on_touchstart: TouchEventCallback;
6
+ on_touchmove: TouchEventCallback;
7
+ on_wheel: WheelEventCallback;
8
+ }
9
+
10
+ // --------------------------------------------------------------------------------------------------------------- //
11
+
12
+ const ATTR_NAME = 'data-scroll-id';
13
+
14
+ const events: {
15
+ [key: string]: ElemEvents;
16
+ } = {};
17
+
18
+ // --------------------------------------------------------------------------------------------------------------- //
19
+
20
+ export function blockScrollEvent(elem: HTMLElement) {
21
+ let id = elem.getAttribute(ATTR_NAME);
22
+
23
+ if (id) {
24
+ return;
25
+ }
26
+ id = 'scroll-id-' + Date.now();
27
+ elem.setAttribute(ATTR_NAME, id);
28
+
29
+ let _client_y: number;
30
+
31
+ const elem_events = {
32
+ on_touchstart: function (e: TouchEvent) {
33
+ if (e.targetTouches.length === 1) {
34
+ _client_y = e.targetTouches[0].clientY;
35
+ }
36
+ },
37
+
38
+ on_touchmove: function (e: TouchEvent) {
39
+ if (e.targetTouches.length !== 1) {
40
+ return;
41
+ }
42
+
43
+ const client_y = e.targetTouches[0].clientY - _client_y;
44
+ if (elem.scrollTop === 0 && client_y > 0) {
45
+ e.preventDefault();
46
+ }
47
+ if (
48
+ elem.scrollHeight - elem.scrollTop <= elem.clientHeight &&
49
+ client_y < 0
50
+ ) {
51
+ e.preventDefault();
52
+ }
53
+ },
54
+
55
+ on_wheel: function (e: WheelEvent) {
56
+ const delta = e.deltaY || e.detail;
57
+
58
+ if (
59
+ (delta < 0 && elem.scrollTop === 0) ||
60
+ (delta > 0 &&
61
+ elem.scrollHeight - elem.clientHeight - elem.scrollTop <= 1)
62
+ ) {
63
+ e.preventDefault();
64
+ }
65
+ },
66
+ };
67
+
68
+ elem.addEventListener('touchstart', elem_events.on_touchstart);
69
+ elem.addEventListener('touchmove', elem_events.on_touchmove);
70
+ elem.addEventListener('wheel', elem_events.on_wheel);
71
+
72
+ events[id] = elem_events;
73
+ }
74
+
75
+ // --------------------------------------------------------------------------------------------------------------- //
76
+
77
+ export function unblock_scroll(elem: HTMLElement) {
78
+ console.log('unblock_scroll');
79
+ const id = elem.getAttribute(ATTR_NAME);
80
+ if (!id) {
81
+ return;
82
+ }
83
+
84
+ elem.removeAttribute(ATTR_NAME);
85
+
86
+ const elem_events = events[id];
87
+
88
+ if (elem_events) {
89
+ elem.removeEventListener('touchstart', elem_events.on_touchstart);
90
+ elem.removeEventListener('touchmove', elem_events.on_touchmove);
91
+ elem.removeEventListener('wheel', elem_events.on_wheel);
92
+ }
93
+
94
+ delete events[id];
95
+ }
@@ -0,0 +1,46 @@
1
+ /** @format */
2
+
3
+ import { AppDispatch } from '../roomStore';
4
+ import { setCurrentRoom } from '../roomStore/roomsSlice';
5
+
6
+ interface XmppConfig {
7
+ xmppSettings?: {
8
+ conference?: string;
9
+ };
10
+ }
11
+
12
+ interface SelectRoomArgs {
13
+ roomJID?: string;
14
+ wasAutoSelected: boolean;
15
+ config: XmppConfig;
16
+ dispatch: AppDispatch;
17
+ }
18
+
19
+ export const chatAutoEnterer = ({
20
+ roomJID,
21
+ wasAutoSelected,
22
+ config,
23
+ dispatch,
24
+ }: SelectRoomArgs): void => {
25
+ console.log('🟡 chatAutoEnterer: Called', { roomJID, wasAutoSelected });
26
+
27
+ if (roomJID) {
28
+ // If roomJID is already a full JID, use it directly
29
+ // Otherwise, construct it from chatId if needed
30
+ const conferenceDomain = config.xmppSettings?.conference ?? '';
31
+ let finalRoomJID = roomJID;
32
+
33
+ if (!roomJID.includes('@') && conferenceDomain) {
34
+ finalRoomJID = `${roomJID}@${conferenceDomain}`;
35
+ console.log('🟡 chatAutoEnterer: Constructed roomJID', finalRoomJID);
36
+ }
37
+
38
+ console.log('🟡 chatAutoEnterer: Setting current room', finalRoomJID);
39
+ dispatch(setCurrentRoom({ roomJID: finalRoomJID }));
40
+ return;
41
+ }
42
+
43
+ console.log('🟡 chatAutoEnterer: No roomJID provided');
44
+ // In React Native, we don't have window.location.search
45
+ // The roomJID should be passed directly via props/config
46
+ };
@@ -0,0 +1,31 @@
1
+ import { IMessage, RoomMember } from '../types/types';
2
+ import { store } from '../roomStore';
3
+ import { fixUnnamedArrayFromApi, getUnnamedUsers } from './getUnnamedUsers';
4
+ import { getUserByXmppUsername } from '../networking/api-requests/roomMembers.api';
5
+
6
+ export const checkUniqueUsers = (messages: IMessage[]) => {
7
+ const unnamedUsers = getUnnamedUsers(messages);
8
+
9
+ if (unnamedUsers.length > 0) {
10
+ const getApiUsers = async () => {
11
+ const fixedUsers = await fixUnnamedArrayFromApi(unnamedUsers);
12
+ return fixedUsers;
13
+ };
14
+ const newUsers = getApiUsers();
15
+ return newUsers;
16
+ }
17
+ };
18
+
19
+ export const checkSingleUser = async (
20
+ usersSet: Record<string, RoomMember>,
21
+ xmppUsername: string
22
+ ) => {
23
+ if (usersSet[xmppUsername]) {return;}
24
+
25
+ const fixedUser = await getUserByXmppUsername(
26
+ xmppUsername,
27
+ store.getState().chatSettingStore.user.token
28
+ );
29
+
30
+ return fixedUser;
31
+ };
@@ -0,0 +1,6 @@
1
+ import { ChatAccessOption } from '../../types/types';
2
+
3
+ export const CHAT_TYPES: ChatAccessOption[] = [
4
+ { name: 'Public', id: 'public' },
5
+ { name: 'Members-only', id: 'group' },
6
+ ];
@@ -0,0 +1,14 @@
1
+ import { Iso639_1Codes } from '../../types/types';
2
+
3
+ type LanguageOption = {
4
+ name: string;
5
+ id: Iso639_1Codes;
6
+ };
7
+
8
+ export const LANGUAGE_OPTIONS: LanguageOption[] = [
9
+ { name: 'English', id: 'en' },
10
+ { name: 'Spanish', id: 'es' },
11
+ { name: 'Portuguese', id: 'pt' },
12
+ { name: 'Haitian Creole', id: 'ht' },
13
+ { name: 'Chinese', id: 'zh' },
14
+ ];
@@ -0,0 +1,3 @@
1
+ export const localStorageConstants = {
2
+ ETHORA_USER: '@ethora/chat-component-user',
3
+ };
@@ -0,0 +1,29 @@
1
+ import {
2
+ CopyIcon,
3
+ DeleteIcon,
4
+ EditIcon,
5
+ ReplyIcon,
6
+ ReportIcon,
7
+ SendCoinIcon,
8
+ } from '../../assets/icons';
9
+ import { SendItemIcon } from '../../assets/images';
10
+
11
+ export const MESSAGE_INTERACTIONS = {
12
+ SEND_COINS: 'Send coins',
13
+ SEND_ITEM: 'Send item',
14
+ REPLY: 'Reply',
15
+ COPY: 'Copy',
16
+ EDIT: 'Edit',
17
+ DELETE: 'Delete',
18
+ REPORT: 'Report',
19
+ };
20
+
21
+ export const MESSAGE_INTERACTIONS_ICONS = {
22
+ SEND_COINS: SendCoinIcon,
23
+ SEND_ITEM: SendItemIcon,
24
+ REPLY: ReplyIcon,
25
+ COPY: CopyIcon,
26
+ EDIT: EditIcon,
27
+ DELETE: DeleteIcon,
28
+ REPORT: ReportIcon,
29
+ };
@@ -0,0 +1,57 @@
1
+ import ChatProfileModal from '../../components/Modals/ChatProfileModal/ChatProfileModal';
2
+ import FilePreviewModal from '../../components/Modals/FilePreviewModal/FilePreviewModal';
3
+ import NewChatModal from '../../components/Modals/NewChatModal/NewChatModal';
4
+ import BlockedUsersModal from '../../components/Modals/SettingsModals/BlockedUsers/BlockedUsersModal';
5
+ import DocumentSharesModal from '../../components/Modals/SettingsModals/DocumentShares/DocumentSharesModal';
6
+ import ManageDataModal from '../../components/Modals/SettingsModals/ManageDataModal/ManageDataModal';
7
+ import ProfileSharesModal from '../../components/Modals/SettingsModals/ProfileShares/ProfileShares';
8
+ import ReferralsModal from '../../components/Modals/SettingsModals/Referrals/Referrals';
9
+ import VisibilityModal from '../../components/Modals/SettingsModals/Visibility/VisibilityModal';
10
+ import UserProfileModal from '../../components/Modals/UserProfileModal/UserProfileModal';
11
+ import UserSettingsModal from '../../components/Modals/UserSettingsModal/UserSettingsModal';
12
+
13
+ export const MODAL_TYPES = {
14
+ SETTINGS: 'settings',
15
+ PROFILE: 'profile',
16
+ CHAT_PROFILE: 'chatprofile',
17
+ MANAGE_DATA: 'managedata',
18
+ VISIBILITY: 'visibility',
19
+
20
+ PROFILE_SHARES: 'profile_shares',
21
+ DOCUMENT_SHARES: 'document_shares',
22
+ BLOCKED_USERS: 'blocked_users',
23
+
24
+ REFERRALS: 'referrals',
25
+
26
+ FILE_PREVIEW: 'file_preview',
27
+
28
+ NEW_CHAT: 'new_chat',
29
+
30
+ // SETTINGS: 'Settings',
31
+ // PROFILE: 'Profile',
32
+ // CHAT_PROFILE: 'Chat Profile',
33
+
34
+ // MANAGE_DATA: 'Manage Data',
35
+ // VISIBILITY: 'Visiblility',
36
+ // PROFILE_SHARES: 'Profile Shares',
37
+ // DOCUMENT_SHARES: 'Document Shares',
38
+ // BLOCKED_USERS: 'Blocked Users',
39
+ // REFERRALS: 'Referrals',
40
+ };
41
+
42
+ export const MODAL_COMPONENTS: Record<
43
+ string,
44
+ React.FC<{ handleCloseModal: () => void }>
45
+ > = {
46
+ [MODAL_TYPES.SETTINGS]: UserSettingsModal,
47
+ [MODAL_TYPES.NEW_CHAT]: NewChatModal,
48
+ [MODAL_TYPES.PROFILE]: UserProfileModal,
49
+ [MODAL_TYPES.CHAT_PROFILE]: ChatProfileModal,
50
+ [MODAL_TYPES.MANAGE_DATA]: ManageDataModal,
51
+ [MODAL_TYPES.VISIBILITY]: VisibilityModal,
52
+ [MODAL_TYPES.REFERRALS]: ReferralsModal,
53
+ [MODAL_TYPES.DOCUMENT_SHARES]: DocumentSharesModal,
54
+ [MODAL_TYPES.PROFILE_SHARES]: ProfileSharesModal,
55
+ [MODAL_TYPES.BLOCKED_USERS]: BlockedUsersModal,
56
+ [MODAL_TYPES.FILE_PREVIEW]: FilePreviewModal,
57
+ };
@@ -0,0 +1,3 @@
1
+ export const CONFERENCE_DOMAIN = '@conference.xmpp.ethoradev.com';
2
+ export const CHAT_DOMAIN = '@xmpp.ethoradev.com';
3
+ export const QRCODE_URL = 'https://beta.ethora.com/app/chat/?chatId=';
@@ -0,0 +1,23 @@
1
+ import { IMessage } from '../types/types';
2
+
3
+ export const createMainMessageForThread = (message: IMessage): string => {
4
+ const data = {
5
+ text: message.body,
6
+ id: message.id,
7
+ userName: message.user.name,
8
+ createdAt: message.date,
9
+ imageLocation: message.location,
10
+ imagePreview: message.locationPreview,
11
+ mimeType: message.mimetype,
12
+ size: '',
13
+ duration: '',
14
+ waveForm: '',
15
+ attachmentId: '',
16
+ wrappable: '',
17
+ nftActionType: '',
18
+ contractAddress: '',
19
+ roomJid: message.roomJid,
20
+ nftId: '',
21
+ };
22
+ return JSON.stringify(data);
23
+ };