@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,122 @@
1
+ import React, { FC, Fragment, useMemo } from 'react';
2
+ import { IConfig, IMessage } from '../../types/types';
3
+ import DateLabel from '../styled/DateLabel';
4
+ import SystemMessage from './SystemMessage';
5
+ import NewMessageLabel from '../styled/NewMessageLabel';
6
+ import {
7
+ Message,
8
+ MessageText,
9
+ MessageTimestamp,
10
+ UserName,
11
+ } from '../styled/StyledComponents';
12
+ import { View } from 'react-native';
13
+
14
+ interface MessageContainerProps {
15
+ CustomMessage?: React.ComponentType<{
16
+ message: IMessage;
17
+ isUser: boolean;
18
+ isReply: boolean;
19
+ className?: string;
20
+ }>;
21
+ CustomDaySeparator?: React.ComponentType<{
22
+ date: Date;
23
+ formattedDate: string;
24
+ }>;
25
+ CustomNewMessageLabel?: React.ComponentType<{
26
+ color?: string;
27
+ }>;
28
+ message: IMessage;
29
+ activeMessage?: IMessage;
30
+ config?: IConfig;
31
+ walletAddress: string;
32
+ isReply: boolean;
33
+ showDateLabel: boolean;
34
+ className?: string;
35
+ }
36
+
37
+ export const MessageContainer: FC<MessageContainerProps> = ({
38
+ CustomMessage,
39
+ CustomDaySeparator,
40
+ CustomNewMessageLabel,
41
+ message,
42
+ activeMessage,
43
+ config,
44
+ walletAddress,
45
+ showDateLabel,
46
+ isReply,
47
+ className,
48
+ }) => {
49
+ const isUser = message.user.id === walletAddress;
50
+
51
+ const messageDate = new Date(message.date);
52
+
53
+ if (message?.isSystemMessage === 'true') {
54
+ const SystemMessageComponent = config?.customSystemMessage;
55
+ return (
56
+ <Fragment key={message.id}>
57
+ {showDateLabel && (
58
+ CustomDaySeparator ? (
59
+ <CustomDaySeparator
60
+ date={messageDate}
61
+ formattedDate={messageDate.toLocaleDateString()}
62
+ />
63
+ ) : (
64
+ <DateLabel date={messageDate} colors={config?.colors} />
65
+ )
66
+ )}
67
+ {SystemMessageComponent ? (
68
+ <SystemMessageComponent message={message} isUser={false} isReply={false} />
69
+ ) : (
70
+ <SystemMessage messageText={message.body} colors={config?.colors} />
71
+ )}
72
+ </Fragment>
73
+ );
74
+ }
75
+
76
+ if (message?.id === 'delimiter-new') {
77
+ return CustomNewMessageLabel ? (
78
+ <CustomNewMessageLabel color={config?.colors?.primary} />
79
+ ) : (
80
+ <NewMessageLabel color={config?.colors?.primary} />
81
+ );
82
+ }
83
+
84
+ const MessageComponent = CustomMessage || Message;
85
+
86
+ return (
87
+ <View key={message.id}>
88
+ {showDateLabel && !activeMessage && message.id !== 'delimiter-new' ? (
89
+ CustomDaySeparator ? (
90
+ <CustomDaySeparator
91
+ date={messageDate}
92
+ formattedDate={messageDate.toLocaleDateString()}
93
+ />
94
+ ) : (
95
+ <DateLabel date={messageDate} colors={config?.colors} />
96
+ )
97
+ ) : null}
98
+ <MessageComponent
99
+ message={message}
100
+ isUser={isUser}
101
+ isReply={isReply}
102
+ className={className}
103
+ >
104
+ {!CustomMessage ? (
105
+ <>
106
+ <MessageTimestamp>
107
+ {messageDate.toLocaleTimeString()}
108
+ </MessageTimestamp>
109
+ <UserName>{message.user.name}: </UserName>
110
+ <MessageText>{message.body}</MessageText>
111
+ </>
112
+ ) : (
113
+ <MessageComponent
114
+ message={message}
115
+ isUser={isUser}
116
+ isReply={isReply}
117
+ />
118
+ )}
119
+ </MessageComponent>
120
+ </View>
121
+ );
122
+ };
@@ -0,0 +1,429 @@
1
+ /** @format */
2
+
3
+ import React, {
4
+ useCallback,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import {
11
+ View,
12
+ StyleSheet,
13
+ FlatList,
14
+ Image,
15
+ NativeSyntheticEvent,
16
+ NativeScrollEvent,
17
+ TouchableOpacity,
18
+ Keyboard,
19
+ ImageSourcePropType,
20
+ } from 'react-native';
21
+ import { IMessage, User, IConfig, IRoom } from '../../types/types';
22
+ import Composing from '../styled/StyledInputComponents/Composing';
23
+ import TreadLabel from '../styled/TreadLabel';
24
+ import { MessageContainer } from './MessageContainer';
25
+ import { useRoomState } from '../../hooks/useRoomState';
26
+ import Loader from '../styled/Loader';
27
+ import { ArowDownIcon } from '../../assets/icons';
28
+ import CustomTypingIndicator from '../styled/StyledInputComponents/CustomTypingIndicator';
29
+
30
+ interface MessageListProps<TMessage extends IMessage> {
31
+ CustomMessage?: React.ComponentType<{
32
+ message: IMessage;
33
+ isUser: boolean;
34
+ isReply: boolean;
35
+ }>;
36
+ CustomDaySeparator?: React.ComponentType<{
37
+ date: Date;
38
+ formattedDate: string;
39
+ }>;
40
+ CustomNewMessageLabel?: React.ComponentType<{
41
+ color?: string;
42
+ }>;
43
+ user: User;
44
+ roomJID: string;
45
+ loadMoreMessages: (
46
+ chatJID: string,
47
+ max: number,
48
+ amount?: number
49
+ ) => Promise<void>;
50
+ loading: boolean;
51
+ config?: IConfig;
52
+ isReply: boolean;
53
+ activeMessage?: IMessage;
54
+ }
55
+
56
+ const MessageList = <TMessage extends IMessage>({
57
+ CustomMessage,
58
+ CustomDaySeparator,
59
+ CustomNewMessageLabel,
60
+ user,
61
+ loadMoreMessages,
62
+ roomJID,
63
+ config,
64
+ loading,
65
+ isReply,
66
+ activeMessage,
67
+ }: MessageListProps<TMessage>) => {
68
+ const { composing, messages, composingList } = useRoomState(roomJID)
69
+ .room! as IRoom;
70
+ const [isUserAtBottom, setIsUserAtBottom] = useState(true);
71
+ const [showNewMessageIndicator, setShowNewMessageIndicator] = useState(false);
72
+ const [isContentOffset, setIsContentOffset] = useState(false);
73
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
74
+ const [isScrollBlocked, setIsScrollBlocked] = useState(false);
75
+ const [hasUserScrolled, setHasUserScrolled] = useState(false);
76
+
77
+ const flatListRef = useRef<FlatList<IMessage>>(null);
78
+
79
+ const addReplyMessages = useMemo(() => {
80
+ return messages.map((message: IMessage) => {
81
+ const newMessage = {
82
+ ...message,
83
+ reply: messages.filter(
84
+ (mess: IMessage) =>
85
+ !!mess.mainMessage &&
86
+ JSON.parse(mess.mainMessage).id === message.id &&
87
+ !mess.deleted &&
88
+ !mess.isDeleted
89
+ ),
90
+ };
91
+ return newMessage;
92
+ });
93
+ }, [messages]);
94
+
95
+ const memoizedMessages = useMemo(() => {
96
+ const nonDeletedMessages = addReplyMessages.filter(
97
+ (item: IMessage) => !item.deleted && !item.isDeleted
98
+ );
99
+
100
+ let filtered: IMessage[];
101
+ if (isReply) {
102
+ filtered = nonDeletedMessages.filter(
103
+ (item: IMessage) =>
104
+ item.roomJid === roomJID &&
105
+ item.isReply &&
106
+ item.isReply === 'true' &&
107
+ item.mainMessage &&
108
+ JSON.parse(item.mainMessage).id === activeMessage?.id
109
+ );
110
+ } else {
111
+ filtered = nonDeletedMessages.filter(
112
+ (item: IMessage) =>
113
+ item.showInChannel === 'true' ||
114
+ ((!item.isReply || item.isReply === 'false') && !item.mainMessage)
115
+ );
116
+ }
117
+
118
+ // Explicit chronological sort by stanza-id timestamp (microseconds-
119
+ // since-epoch encoded in the id). Without this, MAM history that
120
+ // streams in across multiple ticks visibly reorders itself:
121
+ // insertMessageWithDelimiter inserts new arrivals at the slot
122
+ // matching their date.toString() comparison, but redux state can
123
+ // emit intermediate snapshots between dispatches, so the FlatList
124
+ // briefly renders messages out-of-order until the final batch
125
+ // settles. Sorting here makes each render a stable, ordered view —
126
+ // even mid-stream — so the user sees them appear from oldest to
127
+ // newest with no swap animation.
128
+ const sorted = filtered.slice().sort((a, b) => {
129
+ // Pull a numeric timestamp out of the id (server stamps a
130
+ // microsecond timestamp into the stanza-id) — fallback to the
131
+ // `date` ISO string when id isn't numeric (e.g. delimiter rows).
132
+ const aNum = Number(a?.id) || new Date(a?.date as any).getTime() || 0;
133
+ const bNum = Number(b?.id) || new Date(b?.date as any).getTime() || 0;
134
+ return aNum - bNum;
135
+ });
136
+ return sorted;
137
+ }, [addReplyMessages, isReply, roomJID, activeMessage]);
138
+
139
+ const handleLoadMore = useCallback(async () => {
140
+ console.log('🟠 [MessageList] handleLoadMore fired', {
141
+ loading,
142
+ isLoadingMore,
143
+ msgCount: memoizedMessages.length,
144
+ isScrollBlocked,
145
+ });
146
+ if (
147
+ loading ||
148
+ isLoadingMore ||
149
+ !memoizedMessages.length ||
150
+ isScrollBlocked
151
+ ) {
152
+ console.log('🟠 [MessageList] handleLoadMore gated out');
153
+ return;
154
+ }
155
+
156
+ const oldestMessage = memoizedMessages[0];
157
+ if (!oldestMessage || !oldestMessage.id) {
158
+ console.log('🟠 [MessageList] no oldest msg');
159
+ return;
160
+ }
161
+
162
+ setIsLoadingMore(true);
163
+ setIsScrollBlocked(true);
164
+
165
+ try {
166
+ console.log('🟠 [MessageList] calling loadMoreMessages', {
167
+ room: oldestMessage.roomJid || roomJID,
168
+ beforeId: Number(oldestMessage.id),
169
+ oldestId: oldestMessage.id,
170
+ });
171
+ await loadMoreMessages(
172
+ oldestMessage.roomJid || roomJID,
173
+ 15,
174
+ Number(oldestMessage.id)
175
+ );
176
+ console.log('🟠 [MessageList] loadMoreMessages returned');
177
+ } catch (error) {
178
+ console.error('🔴 [MessageList] loadMoreMessages threw:', error);
179
+ } finally {
180
+ setTimeout(() => {
181
+ setIsLoadingMore(false);
182
+ setIsScrollBlocked(false);
183
+ }, 500);
184
+ }
185
+ }, [
186
+ memoizedMessages,
187
+ loadMoreMessages,
188
+ loading,
189
+ isLoadingMore,
190
+ isScrollBlocked,
191
+ roomJID,
192
+ ]);
193
+
194
+ const dataMessages = useMemo(() => {
195
+ return memoizedMessages.slice().reverse();
196
+ }, [memoizedMessages]);
197
+
198
+ const renderMessage = useCallback(
199
+ ({ item, index }: { item: IMessage; index: number }) => {
200
+ const messageDate = new Date(item.date).toDateString();
201
+ let showDateLabel = false;
202
+
203
+ const nextMessage =
204
+ index < dataMessages.length - 1 ? dataMessages[index + 1] : null;
205
+ const nextMessageDate = nextMessage
206
+ ? new Date(nextMessage.date).toDateString()
207
+ : null;
208
+
209
+ if (!nextMessage || messageDate !== nextMessageDate) {
210
+ showDateLabel = true;
211
+ }
212
+
213
+ return (
214
+ <MessageContainer
215
+ CustomMessage={CustomMessage}
216
+ CustomDaySeparator={CustomDaySeparator}
217
+ CustomNewMessageLabel={CustomNewMessageLabel}
218
+ message={item}
219
+ activeMessage={activeMessage}
220
+ config={config}
221
+ walletAddress={user.xmppUsername || user.walletAddress}
222
+ isReply={isReply}
223
+ showDateLabel={showDateLabel}
224
+ />
225
+ );
226
+ },
227
+ [
228
+ activeMessage,
229
+ config,
230
+ user.xmppUsername,
231
+ user.walletAddress,
232
+ isReply,
233
+ memoizedMessages.length,
234
+ ]
235
+ );
236
+
237
+ const scrollToBottom = useCallback(() => {
238
+ if (flatListRef.current) {
239
+ flatListRef.current.scrollToOffset({
240
+ offset: 0,
241
+ animated: true,
242
+ });
243
+ }
244
+ }, [flatListRef]);
245
+
246
+ const handleContentSizeChange = useCallback(() => {
247
+ if (isContentOffset) {
248
+ scrollToBottom();
249
+ setIsContentOffset(false);
250
+ }
251
+ if (!isLoadingMore && flatListRef.current && isUserAtBottom) {
252
+ scrollToBottom();
253
+ } else if (!isLoadingMore && hasUserScrolled) {
254
+ setShowNewMessageIndicator(true);
255
+ }
256
+ }, [isUserAtBottom, scrollToBottom, isLoadingMore, hasUserScrolled]);
257
+
258
+ const handleScroll = useCallback(
259
+ (event: NativeSyntheticEvent<NativeScrollEvent>) => {
260
+ const contentOffset = event.nativeEvent.contentOffset.y;
261
+
262
+ Keyboard.dismiss();
263
+
264
+ if (contentOffset > 150 && !hasUserScrolled) {
265
+ setHasUserScrolled(true);
266
+ }
267
+
268
+ if (contentOffset < 150) {
269
+ setIsContentOffset(true);
270
+ setShowNewMessageIndicator(false);
271
+ setIsUserAtBottom(true);
272
+ } else {
273
+ setIsContentOffset(false);
274
+ if (hasUserScrolled) {
275
+ setShowNewMessageIndicator(true);
276
+ }
277
+ setIsUserAtBottom(false);
278
+ }
279
+ },
280
+ [hasUserScrolled]
281
+ );
282
+
283
+ const handleLayout = () => {
284
+ setIsContentOffset(true);
285
+ setIsUserAtBottom(true);
286
+ };
287
+
288
+ const handleNewMessageIndicatorPress = () => {
289
+ setShowNewMessageIndicator(false);
290
+ setIsUserAtBottom(true);
291
+ scrollToBottom();
292
+ };
293
+
294
+ useEffect(() => {
295
+ setHasUserScrolled(false);
296
+ setShowNewMessageIndicator(false);
297
+ setIsUserAtBottom(true);
298
+ }, [roomJID]);
299
+
300
+ const BackgroundImage = useMemo(() => {
301
+ const image = config?.backgroundChat?.image;
302
+
303
+ if (image) {
304
+ if (typeof image === 'function') {
305
+ const SvgComponent = image as React.FC<React.SVGProps<SVGSVGElement>>;
306
+ return <SvgComponent width="100%" />;
307
+ } else {
308
+ return <Image source={image as ImageSourcePropType} />;
309
+ }
310
+ }
311
+
312
+ return <View />;
313
+ }, [config?.backgroundChat?.image]);
314
+
315
+ return (
316
+ <View
317
+ style={[
318
+ styles.container,
319
+ { backgroundColor: config?.backgroundChat?.color || '#F3F6FC' },
320
+ ]}
321
+ >
322
+ <View style={styles.backgroundImageContainer}>{BackgroundImage}</View>
323
+ {activeMessage && (
324
+ <View>
325
+ {CustomMessage && (
326
+ <CustomMessage
327
+ message={activeMessage}
328
+ isUser={activeMessage.user.id === user.walletAddress}
329
+ isReply={isReply}
330
+ />
331
+ )}
332
+ <TreadLabel reply={memoizedMessages.length} colors={config?.colors} />
333
+ </View>
334
+ )}
335
+ <FlatList
336
+ ref={flatListRef}
337
+ data={memoizedMessages.slice().reverse()}
338
+ renderItem={renderMessage}
339
+ keyExtractor={(item) => item.id.toString()}
340
+ onEndReached={handleLoadMore}
341
+ onScroll={handleScroll}
342
+ onContentSizeChange={handleContentSizeChange}
343
+ onEndReachedThreshold={0.1}
344
+ scrollEventThrottle={16}
345
+ onLayout={handleLayout}
346
+ inverted={true}
347
+ contentContainerStyle={styles.flatListContent}
348
+ ListFooterComponent={
349
+ loading && memoizedMessages.length > 15 ? (
350
+ <Loader color={config?.colors?.primary} />
351
+ ) : null
352
+ }
353
+ />
354
+ {showNewMessageIndicator && (
355
+ <TouchableOpacity
356
+ style={[
357
+ styles.newMessageIndicator,
358
+ { backgroundColor: config?.colors?.secondary },
359
+ ]}
360
+ onPress={handleNewMessageIndicatorPress}
361
+ >
362
+ <ArowDownIcon />
363
+ </TouchableOpacity>
364
+ )}
365
+ {config?.customTypingIndicator?.enabled && composing && (
366
+ <CustomTypingIndicator
367
+ usersTyping={composingList || ['User']}
368
+ text={config.customTypingIndicator.text}
369
+ position={config.customTypingIndicator.position || 'bottom'}
370
+ styles={config.customTypingIndicator.styles}
371
+ customComponent={config.customTypingIndicator.customComponent}
372
+ isVisible={composing}
373
+ />
374
+ )}
375
+
376
+ {!config?.customTypingIndicator?.enabled &&
377
+ config?.disableHeader &&
378
+ composing && <Composing usersTyping={composingList || ['User']} />}
379
+ </View>
380
+ );
381
+ };
382
+
383
+ export default MessageList;
384
+
385
+ const styles = StyleSheet.create({
386
+ container: {
387
+ flex: 1,
388
+ width: '100%',
389
+ },
390
+ flatListContent: {
391
+ paddingHorizontal: 10,
392
+ paddingBottom: 10,
393
+ },
394
+ messageList: {
395
+ paddingHorizontal: 10,
396
+ flexGrow: 1,
397
+ backgroundColor: '#434343',
398
+ },
399
+ backgroundImageContainer: {
400
+ width: '100%',
401
+ position: 'absolute',
402
+ top: 0,
403
+ left: 0,
404
+ right: 0,
405
+ bottom: 0,
406
+ zIndex: -1,
407
+ },
408
+ image: {
409
+ position: 'absolute',
410
+ top: 0,
411
+ left: 0,
412
+ width: '100%',
413
+ height: '100%',
414
+ },
415
+ newMessageIndicator: {
416
+ position: 'absolute',
417
+ width: 40,
418
+ height: 40,
419
+ bottom: 20,
420
+ right: 20,
421
+ backgroundColor: '#007AFF',
422
+ padding: 10,
423
+ borderRadius: 20,
424
+ },
425
+ newMessageText: {
426
+ color: '#fff',
427
+ fontWeight: 'bold',
428
+ },
429
+ });
@@ -0,0 +1,49 @@
1
+ import { Text, View } from 'react-native';
2
+ import { NoMessages } from '../../assets/icons';
3
+
4
+ const NoMessagesPlaceholder = () => {
5
+ return (
6
+ <View
7
+ style={{
8
+ display: 'flex',
9
+ justifyContent: 'center',
10
+ alignItems: 'center',
11
+ }}
12
+ >
13
+ <View style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
14
+ {/* <NoMessages /> */}
15
+ <View
16
+ style={{
17
+ flexDirection: 'column',
18
+ gap: 8,
19
+ padding: 16,
20
+ justifyContent: 'center',
21
+ }}
22
+ >
23
+ <Text
24
+ style={{
25
+ color: '#a3a3a3',
26
+ textAlign: 'center',
27
+ fontSize: 16,
28
+ fontWeight: '600',
29
+ }}
30
+ >
31
+ This chat is empty
32
+ </Text>
33
+ <Text
34
+ style={{
35
+ color: '#a3a3a3',
36
+ textAlign: 'center',
37
+ fontSize: 14,
38
+ fontWeight: '400',
39
+ }}
40
+ >
41
+ Be the first one to start it.
42
+ </Text>
43
+ </View>
44
+ </View>
45
+ </View>
46
+ );
47
+ };
48
+
49
+ export default NoMessagesPlaceholder;