@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.
- package/LICENSE +661 -0
- package/README.md +148 -0
- package/empty-shim.js +15 -0
- package/index.js +13 -0
- package/metro.js +72 -0
- package/package.json +99 -0
- package/src/api.config.ts +34 -0
- package/src/assets/BG.png +0 -0
- package/src/assets/BG.tsx +56 -0
- package/src/assets/File.svg +4 -0
- package/src/assets/ForzaCare.png +0 -0
- package/src/assets/ForzaCare.tsx +27 -0
- package/src/assets/icons.tsx +685 -0
- package/src/assets/images.tsx +41 -0
- package/src/assets/test.xml +243 -0
- package/src/components/AuthForms/Login.tsx +295 -0
- package/src/components/AuthForms/LoginAnonym.tsx +68 -0
- package/src/components/AuthForms/Register.tsx +8 -0
- package/src/components/ContextMenu/ContextMenuComponents.tsx +87 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +244 -0
- package/src/components/ExampleComponents/CustomMessage.tsx +92 -0
- package/src/components/ExampleComponents/CustomTypingIndicatorExamples.tsx +239 -0
- package/src/components/Header/HeaderRoomList.tsx +104 -0
- package/src/components/InputComponents/AudioInput.tsx +24 -0
- package/src/components/InputComponents/AudioRecorder.tsx +100 -0
- package/src/components/InputComponents/MediaInput.tsx +114 -0
- package/src/components/InputComponents/RecordingIndicator.tsx +77 -0
- package/src/components/InputComponents/Search.tsx +135 -0
- package/src/components/InputComponents/TextInput.tsx +66 -0
- package/src/components/MainComponents/ChatHeader.tsx +163 -0
- package/src/components/MainComponents/ChatRoom.tsx +392 -0
- package/src/components/MainComponents/ChatWrapper.tsx +318 -0
- package/src/components/MainComponents/ChooseChatMessage.tsx +44 -0
- package/src/components/MainComponents/ConnectionBanner.tsx +33 -0
- package/src/components/MainComponents/EditWrapper.tsx +72 -0
- package/src/components/MainComponents/LoginWrapper.tsx +134 -0
- package/src/components/MainComponents/MediaMessage.tsx +55 -0
- package/src/components/MainComponents/MessageContainer.tsx +122 -0
- package/src/components/MainComponents/MessageList.tsx +429 -0
- package/src/components/MainComponents/NoMessagesPlaceholder.tsx +49 -0
- package/src/components/MainComponents/ProfileImagePlaceholder.tsx +143 -0
- package/src/components/MainComponents/RNStyled.tsx +34 -0
- package/src/components/MainComponents/ReduxWrapper.tsx +40 -0
- package/src/components/MainComponents/RoomList.tsx +246 -0
- package/src/components/MainComponents/Select.tsx +165 -0
- package/src/components/MainComponents/Switch.tsx +73 -0
- package/src/components/MainComponents/SystemMessage.tsx +42 -0
- package/src/components/Menu/HeaderRoomListMenu.tsx +169 -0
- package/src/components/MenuRoom/MenuRoom.tsx +46 -0
- package/src/components/MessageBubble/Avatar.tsx +84 -0
- package/src/components/MessageBubble/BottomReplyContainer.tsx +117 -0
- package/src/components/MessageBubble/CustomDivider.tsx +10 -0
- package/src/components/MessageBubble/DeletedMessage.tsx +36 -0
- package/src/components/MessageBubble/Message.tsx +400 -0
- package/src/components/MessageBubble/MessageInteractions.tsx +247 -0
- package/src/components/MessageBubble/MessageReaction.tsx +149 -0
- package/src/components/MessageBubble/MessageReply.tsx +50 -0
- package/src/components/MessageBubble/MessageTranslations.tsx +30 -0
- package/src/components/Modals/AddMembersModal/AddMembersModal.tsx +142 -0
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +471 -0
- package/src/components/Modals/ChatProfileModal/DeleteChatModal.tsx +91 -0
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +298 -0
- package/src/components/Modals/Modal/Modal.tsx +52 -0
- package/src/components/Modals/ModalHeaderComponent.tsx +41 -0
- package/src/components/Modals/ModalSelectMedia/ModalSelectMedia.tsx +285 -0
- package/src/components/Modals/ModalWrapper/ModalWrapper.tsx +103 -0
- package/src/components/Modals/NewChatModal/ModalContent.tsx +151 -0
- package/src/components/Modals/NewChatModal/NewChatModal.tsx +391 -0
- package/src/components/Modals/NewChatModal/UpdNewChatModal.tsx +136 -0
- package/src/components/Modals/SelectUsersModal/SelectUsersModal.tsx +134 -0
- package/src/components/Modals/SettingsModals/BlockedUsers/BlockedUsersModal.tsx +63 -0
- package/src/components/Modals/SettingsModals/DocumentShares/DocumentSharesModal.tsx +115 -0
- package/src/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.tsx +121 -0
- package/src/components/Modals/SettingsModals/ManageDataModal/StyledComponents.ts +0 -0
- package/src/components/Modals/SettingsModals/ProfileShares/ProfileShares.tsx +65 -0
- package/src/components/Modals/SettingsModals/Referrals/Referrals.tsx +90 -0
- package/src/components/Modals/SettingsModals/SharedStyledComponents.ts +74 -0
- package/src/components/Modals/SettingsModals/Visibility/RadioInput.tsx +59 -0
- package/src/components/Modals/SettingsModals/Visibility/StyledComponents.ts +32 -0
- package/src/components/Modals/SettingsModals/Visibility/VisibilityModal.tsx +146 -0
- package/src/components/Modals/UserProfileModal/EditUserModal.tsx +163 -0
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +410 -0
- package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +85 -0
- package/src/components/Modals/styledModalComponents.tsx +181 -0
- package/src/components/OperationalModal/OperationalModal.tsx +78 -0
- package/src/components/RoomComponents/ChatRoomItem.tsx +219 -0
- package/src/components/RoomComponents/LastMessageItem.tsx +47 -0
- package/src/components/RoomComponents/styled/LastAudioMessage.tsx +62 -0
- package/src/components/RoomComponents/styled/LastMessageEmoji.tsx +26 -0
- package/src/components/RoomComponents/styled/LastMessageFile.tsx +54 -0
- package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +54 -0
- package/src/components/RoomComponents/styled/LastMessageVideo.tsx +70 -0
- package/src/components/RoomComponents/styled/LastTextMessage.tsx +65 -0
- package/src/components/RoomComponents/styled/StyledRoomComponents.ts +53 -0
- package/src/components/SortDropDown/index.tsx +76 -0
- package/src/components/Thread/ThreadHeader.tsx +41 -0
- package/src/components/Thread/ThreadWrapper.tsx +270 -0
- package/src/components/Toast/Toast.tsx +128 -0
- package/src/components/Toast/index.ts +13 -0
- package/src/components/UsersList/StyledComponents.ts +30 -0
- package/src/components/UsersList/UsersList.tsx +112 -0
- package/src/components/styled/ActionButton.tsx +40 -0
- package/src/components/styled/AudioMessage.tsx +82 -0
- package/src/components/styled/Button.tsx +107 -0
- package/src/components/styled/ChatWrapperBox.tsx +9 -0
- package/src/components/styled/DateLabel.tsx +77 -0
- package/src/components/styled/Loader.tsx +65 -0
- package/src/components/styled/MediaFilePreview.tsx +42 -0
- package/src/components/styled/MediaModal.tsx +54 -0
- package/src/components/styled/MessageImage.tsx +54 -0
- package/src/components/styled/NewMessageLabel.tsx +50 -0
- package/src/components/styled/RoomListComponents/index.tsx +164 -0
- package/src/components/styled/SendInput.tsx +297 -0
- package/src/components/styled/StyledComponents.ts +320 -0
- package/src/components/styled/StyledInput.tsx +101 -0
- package/src/components/styled/StyledInputComponents/AudioComponents.tsx +1 -0
- package/src/components/styled/StyledInputComponents/Composing.tsx +97 -0
- package/src/components/styled/StyledInputComponents/CustomTypingIndicator.tsx +261 -0
- package/src/components/styled/StyledInputComponents/MediaComponents.tsx +90 -0
- package/src/components/styled/StyledInputComponents/StyledInputComponents.tsx +131 -0
- package/src/components/styled/StyledInputComponents/VideoComponents.tsx +49 -0
- package/src/components/styled/TreadLabel.tsx +50 -0
- package/src/components/styled/UnsupportedType.tsx +95 -0
- package/src/components/styled/VideoMessage.tsx +68 -0
- package/src/context/MessageNotificationContext.tsx +335 -0
- package/src/context/PushNotificationProvider.tsx +78 -0
- package/src/context/ToastContext.tsx +63 -0
- package/src/context/toastProvider.tsx +0 -0
- package/src/context/xmppProvider.tsx +364 -0
- package/src/helpers/block_scroll.ts +95 -0
- package/src/helpers/chatAutoEnterer.ts +46 -0
- package/src/helpers/checkUniqueUsers.tsx +31 -0
- package/src/helpers/constants/CHAT_TYPES.ts +6 -0
- package/src/helpers/constants/LANGUAGE_OPTIONS.ts +14 -0
- package/src/helpers/constants/LOCAL_STORAGE.ts +3 -0
- package/src/helpers/constants/MESSAGE_INTERACTIONS.ts +29 -0
- package/src/helpers/constants/MODAL_TYPES.ts +57 -0
- package/src/helpers/constants/PLATFORM_CONSTANTS.ts +3 -0
- package/src/helpers/createMainMessageForThread.ts +23 -0
- package/src/helpers/createMessageFromXml.ts +118 -0
- package/src/helpers/createRoomFromApi.tsx +27 -0
- package/src/helpers/createUserNameFromSetUser.ts +15 -0
- package/src/helpers/dateComparison.ts +12 -0
- package/src/helpers/debounce.ts +10 -0
- package/src/helpers/ensureScopedChatCache.ts +61 -0
- package/src/helpers/extractUniqueMembersFromRooms.ts +45 -0
- package/src/helpers/formatNumberWithCommas.ts +3 -0
- package/src/helpers/getBooleanFromString.ts +7 -0
- package/src/helpers/getDataFromXml.ts +80 -0
- package/src/helpers/getNumberFromString.ts +5 -0
- package/src/helpers/getRoomsWithRetry.ts +45 -0
- package/src/helpers/getTintedColor.ts +28 -0
- package/src/helpers/getUnnamedUsers.ts +39 -0
- package/src/helpers/handleCopyClick.ts +3 -0
- package/src/helpers/handleXmppError.ts +14 -0
- package/src/helpers/hashcolor.ts +34 -0
- package/src/helpers/historyPreloadScheduler.ts +264 -0
- package/src/helpers/initRoomsPresence.ts +32 -0
- package/src/helpers/initXmppRooms.tsx +71 -0
- package/src/helpers/insertMessageWithDelimiter.ts +96 -0
- package/src/helpers/isChatIdPresentInArray.ts +54 -0
- package/src/helpers/localStorageUser.ts +9 -0
- package/src/helpers/markdownParser.tsx +295 -0
- package/src/helpers/nanoToMs.ts +3 -0
- package/src/helpers/parseMessageBody.tsx +516 -0
- package/src/helpers/parseUrls.tsx +42 -0
- package/src/helpers/resolveInitBeforeLoadUser.ts +240 -0
- package/src/helpers/storeConsole.ts +26 -0
- package/src/helpers/transformTranslatations.ts +16 -0
- package/src/helpers/updateMessagesTillLast.tsx +104 -0
- package/src/helpers/updatedChatLastTimestamps.ts +29 -0
- package/src/helpers/validator.ts +20 -0
- package/src/helpers/walletUsername.ts +12 -0
- package/src/hooks/hooks.ts +6 -0
- package/src/hooks/useChatSettingState.tsx +38 -0
- package/src/hooks/useChatWrapperInit.ts +266 -0
- package/src/hooks/useComposing.tsx +59 -0
- package/src/hooks/useCustomTypingIndicator.tsx +193 -0
- package/src/hooks/useEventHandlers.tsx +81 -0
- package/src/hooks/useGetNewArchRoom.tsx +57 -0
- package/src/hooks/useHeapSender.tsx +88 -0
- package/src/hooks/useLocalStorage.tsx +53 -0
- package/src/hooks/useLogout.tsx +31 -0
- package/src/hooks/useMessageHeapState.tsx +11 -0
- package/src/hooks/useMessageLoaderQueue.tsx +90 -0
- package/src/hooks/useMessageQueue.ts +146 -0
- package/src/hooks/usePendingNotification.ts +49 -0
- package/src/hooks/usePrevious.tsx +15 -0
- package/src/hooks/usePushNotifications.ts +106 -0
- package/src/hooks/useQRCodeChatHandler.ts +58 -0
- package/src/hooks/useRoomInitialization.tsx +160 -0
- package/src/hooks/useRoomState.tsx +47 -0
- package/src/hooks/useRoomUrl.tsx +41 -0
- package/src/hooks/useSendMessage.tsx +306 -0
- package/src/hooks/useUnreadMessagesCounter.ts +55 -0
- package/src/hooks/useXmppInitialization.ts +148 -0
- package/src/main.ts +45 -0
- package/src/networking/api-requests/auth.api.ts +140 -0
- package/src/networking/api-requests/push.api.ts +55 -0
- package/src/networking/api-requests/roomMembers.api.ts +20 -0
- package/src/networking/api-requests/rooms.api.ts +142 -0
- package/src/networking/api-requests/user.api.ts +65 -0
- package/src/networking/apiClient.ts +149 -0
- package/src/networking/helpers/refreshChecker.ts +13 -0
- package/src/networking/stanzaHandlers.ts +418 -0
- package/src/networking/xmpp/actionSetTimestampToPrivateStore.xmpp.ts +37 -0
- package/src/networking/xmpp/allRoomPresences.xmpp.ts +10 -0
- package/src/networking/xmpp/createPrivateRoom.xmpp.ts +26 -0
- package/src/networking/xmpp/createRoom.xmpp.ts +35 -0
- package/src/networking/xmpp/createRoomPresence.xmpp.ts +74 -0
- package/src/networking/xmpp/createTimeoutPromise.xmpp.ts +15 -0
- package/src/networking/xmpp/deleteMessage.xmpp.ts +19 -0
- package/src/networking/xmpp/editMessage.xmpp.ts +46 -0
- package/src/networking/xmpp/getChatsPrivateStoreRequest.xmpp.ts +57 -0
- package/src/networking/xmpp/getHistory.xmpp.ts +140 -0
- package/src/networking/xmpp/getLastMessageArchive.xmpp.ts +24 -0
- package/src/networking/xmpp/getRoomInfo.xmpp.ts +15 -0
- package/src/networking/xmpp/getRoomMembers.xmpp.ts +13 -0
- package/src/networking/xmpp/getRooms.xmpp.ts +43 -0
- package/src/networking/xmpp/getRoomsPaged.xmpp.ts +59 -0
- package/src/networking/xmpp/handlePong.xmpp.ts +7 -0
- package/src/networking/xmpp/handleStanzas.xmpp.ts +55 -0
- package/src/networking/xmpp/inviteRoomRequest.xmpp.ts +25 -0
- package/src/networking/xmpp/leaveTheRoom.xmpp.ts +15 -0
- package/src/networking/xmpp/listenerManager.ts +116 -0
- package/src/networking/xmpp/presenceInRoom.xmpp.ts +57 -0
- package/src/networking/xmpp/roomConfig.xmpp.ts +71 -0
- package/src/networking/xmpp/sendMediaMessage.xmpp.ts +97 -0
- package/src/networking/xmpp/sendMessageReaction.xmpp.ts +41 -0
- package/src/networking/xmpp/sendPing.xmpp.ts +21 -0
- package/src/networking/xmpp/sendTextMessage.xmpp.ts +99 -0
- package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +44 -0
- package/src/networking/xmpp/sendTypingRequest.xmpp.ts +26 -0
- package/src/networking/xmpp/setChatsPrivateStoreRequest.xmpp.ts +48 -0
- package/src/networking/xmpp/setMeAsOwner.xmpp.ts +52 -0
- package/src/networking/xmpp/setRoomImage.xmpp.ts +26 -0
- package/src/networking/xmpp/setVCard.xmpp.ts +19 -0
- package/src/networking/xmpp/subscribeToRoomMessages.xmpp.ts +75 -0
- package/src/networking/xmppClient.ts +603 -0
- package/src/roomStore/Middleware/logoutMiddleware.tsx +24 -0
- package/src/roomStore/Middleware/newMessageMidlleware.tsx +87 -0
- package/src/roomStore/Middleware/reactionsMiddleware.tsx +77 -0
- package/src/roomStore/Middleware/testMiddleware.tsx +20 -0
- package/src/roomStore/Middleware/unreadMidlleware.tsx +58 -0
- package/src/roomStore/chatSettingsSlice.ts +178 -0
- package/src/roomStore/index.ts +68 -0
- package/src/roomStore/persistence.ts +152 -0
- package/src/roomStore/roomHeapSlice.ts +37 -0
- package/src/roomStore/roomsSlice.ts +353 -0
- package/src/services/pushNotifications.ts +170 -0
- package/src/services/pushSubscriptionService.ts +145 -0
- package/src/types/messages.ts +61 -0
- package/src/types/models/action.model.ts +30 -0
- package/src/types/models/config.model.ts +16 -0
- package/src/types/models/customComponents.model.ts +53 -0
- package/src/types/models/language.model.ts +11 -0
- package/src/types/models/media.model.ts +5 -0
- package/src/types/models/message.model.ts +69 -0
- package/src/types/models/modal.model.ts +9 -0
- package/src/types/models/room.model.ts +120 -0
- package/src/types/models/user.model.ts +90 -0
- package/src/types/models/xmpp.model.ts +155 -0
- package/src/types/types.ts +505 -0
- package/src/utils/PriorityQueue.ts +47 -0
- package/src/utils/actionHelpers.ts +40 -0
- package/src/utils/actionLogger.ts +64 -0
- package/src/utils/clientRegistry.ts +80 -0
- package/src/utils/devLogger.ts +197 -0
- package/src/utils/messageNotificationManager.ts +108 -0
- package/src/utils/resendMessage.ts +87 -0
- package/src/utils/toastEmitter.ts +27 -0
- package/src/utils/xmppMemoryMonitor.ts +63 -0
- 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,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,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,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
|
+
}
|