@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,80 @@
|
|
|
1
|
+
import XmppClient from '../networking/xmppClient';
|
|
2
|
+
import { xmppSettingsInterface } from '../types/types';
|
|
3
|
+
|
|
4
|
+
let currentClient: XmppClient | null = null;
|
|
5
|
+
let currentClientKey = '';
|
|
6
|
+
const initLocks = new Map<string, Promise<XmppClient>>();
|
|
7
|
+
|
|
8
|
+
const DEFAULT_DEV_SERVER = 'xmpp.chat.ethora.com';
|
|
9
|
+
|
|
10
|
+
export function buildXmppClientKey(
|
|
11
|
+
username: string,
|
|
12
|
+
xmppSettings?: xmppSettingsInterface
|
|
13
|
+
): string {
|
|
14
|
+
const devServer = xmppSettings?.devServer || DEFAULT_DEV_SERVER;
|
|
15
|
+
const host = xmppSettings?.host || '';
|
|
16
|
+
const conference = xmppSettings?.conference || '';
|
|
17
|
+
return `${username}|${devServer}|${host}|${conference}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isXmppClientReusable(client: XmppClient | null): boolean {
|
|
21
|
+
if (!client) {return false;}
|
|
22
|
+
return client.status === 'online' || client.status === 'connecting';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function setGlobalXmppClient(
|
|
26
|
+
client: XmppClient | null,
|
|
27
|
+
key?: string
|
|
28
|
+
): void {
|
|
29
|
+
currentClient = client;
|
|
30
|
+
currentClientKey = client ? key || currentClientKey || '' : '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getGlobalXmppClient(): XmppClient | null {
|
|
34
|
+
return currentClient;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function getGlobalXmppClientKey(): string {
|
|
38
|
+
return currentClientKey;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getReusableXmppClientByKey(key: string): XmppClient | null {
|
|
42
|
+
if (!key || key !== currentClientKey) {return null;}
|
|
43
|
+
if (!isXmppClientReusable(currentClient)) {return null;}
|
|
44
|
+
return currentClient;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function withXmppClientInitLock(
|
|
48
|
+
key: string,
|
|
49
|
+
init: () => Promise<XmppClient>
|
|
50
|
+
): Promise<XmppClient> {
|
|
51
|
+
const existing = initLocks.get(key);
|
|
52
|
+
if (existing) {return existing;}
|
|
53
|
+
|
|
54
|
+
const createdPromise = init().finally(() => {
|
|
55
|
+
if (initLocks.get(key) === createdPromise) {
|
|
56
|
+
initLocks.delete(key);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
initLocks.set(key, createdPromise);
|
|
61
|
+
return createdPromise;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function requireXmppClient(): XmppClient {
|
|
65
|
+
if (!currentClient) {
|
|
66
|
+
throw new Error('XMPP client is not initialized');
|
|
67
|
+
}
|
|
68
|
+
return currentClient;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default {
|
|
72
|
+
buildXmppClientKey,
|
|
73
|
+
isXmppClientReusable,
|
|
74
|
+
setGlobalXmppClient,
|
|
75
|
+
getGlobalXmppClient,
|
|
76
|
+
getGlobalXmppClientKey,
|
|
77
|
+
getReusableXmppClientByKey,
|
|
78
|
+
withXmppClientInitLock,
|
|
79
|
+
requireXmppClient,
|
|
80
|
+
};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev-only log bus used by the testbed `Logs` tab. Stores a bounded
|
|
3
|
+
* ring-buffer of entries that arrive from:
|
|
4
|
+
* - `console.log/info/warn/error` (patched in `installDevLogCapture`)
|
|
5
|
+
* - `axios` request/response interceptors (HTTP)
|
|
6
|
+
* - `XmppClient.attachEventListeners` (XMPP stanzas in/out)
|
|
7
|
+
*
|
|
8
|
+
* Pure module-level singleton — no React, callable from anywhere
|
|
9
|
+
* (reducers, services, helpers). React side reads via
|
|
10
|
+
* `useDevLogs()` which uses `useSyncExternalStore`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type LogKind = 'log' | 'info' | 'warn' | 'error' | 'http' | 'xmpp' | 'rn';
|
|
14
|
+
|
|
15
|
+
export interface LogEntry {
|
|
16
|
+
id: number;
|
|
17
|
+
kind: LogKind;
|
|
18
|
+
ts: number;
|
|
19
|
+
message: string;
|
|
20
|
+
details?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const MAX_ENTRIES = 500;
|
|
24
|
+
|
|
25
|
+
let entries: LogEntry[] = [];
|
|
26
|
+
let listeners: Set<() => void> = new Set();
|
|
27
|
+
let nextId = 1;
|
|
28
|
+
|
|
29
|
+
export function pushLog(
|
|
30
|
+
kind: LogKind,
|
|
31
|
+
message: string,
|
|
32
|
+
details?: any
|
|
33
|
+
): void {
|
|
34
|
+
const detailsStr =
|
|
35
|
+
details === undefined
|
|
36
|
+
? undefined
|
|
37
|
+
: typeof details === 'string'
|
|
38
|
+
? details
|
|
39
|
+
: safeStringify(details);
|
|
40
|
+
entries = [
|
|
41
|
+
...entries,
|
|
42
|
+
{ id: nextId++, kind, ts: Date.now(), message, details: detailsStr },
|
|
43
|
+
];
|
|
44
|
+
if (entries.length > MAX_ENTRIES) {
|
|
45
|
+
entries = entries.slice(-MAX_ENTRIES);
|
|
46
|
+
}
|
|
47
|
+
// Defer subscriber notifications via microtask so a `pushLog` called
|
|
48
|
+
// during a React render (e.g. via the captured `console.log`) doesn't
|
|
49
|
+
// trigger a setState-during-render warning in subscribers using
|
|
50
|
+
// `useSyncExternalStore`.
|
|
51
|
+
const notify = () =>
|
|
52
|
+
listeners.forEach((fn) => {
|
|
53
|
+
try {
|
|
54
|
+
fn();
|
|
55
|
+
} catch {
|
|
56
|
+
/* noop */
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (typeof queueMicrotask === 'function') {
|
|
60
|
+
queueMicrotask(notify);
|
|
61
|
+
} else {
|
|
62
|
+
Promise.resolve().then(notify);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function clearLogs(): void {
|
|
67
|
+
entries = [];
|
|
68
|
+
listeners.forEach((fn) => fn());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getLogs(): LogEntry[] {
|
|
72
|
+
return entries;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function subscribeLogs(fn: () => void): () => void {
|
|
76
|
+
listeners.add(fn);
|
|
77
|
+
return () => {
|
|
78
|
+
listeners.delete(fn);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function safeStringify(value: any): string {
|
|
83
|
+
try {
|
|
84
|
+
return JSON.stringify(
|
|
85
|
+
value,
|
|
86
|
+
(_key, v) => {
|
|
87
|
+
if (typeof v === 'bigint') {return v.toString();}
|
|
88
|
+
if (v instanceof Error)
|
|
89
|
+
{return { name: v.name, message: v.message, stack: v.stack };}
|
|
90
|
+
return v;
|
|
91
|
+
},
|
|
92
|
+
2
|
|
93
|
+
);
|
|
94
|
+
} catch {
|
|
95
|
+
try {
|
|
96
|
+
return String(value);
|
|
97
|
+
} catch {
|
|
98
|
+
return '[unstringifiable]';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------
|
|
104
|
+
// console.* capture
|
|
105
|
+
// ---------------------------------------------------------------------
|
|
106
|
+
let consolePatched = false;
|
|
107
|
+
|
|
108
|
+
export function installConsoleCapture() {
|
|
109
|
+
if (consolePatched) {return;}
|
|
110
|
+
consolePatched = true;
|
|
111
|
+
(['log', 'info', 'warn', 'error'] as const).forEach((level) => {
|
|
112
|
+
const original = (console as any)[level].bind(console);
|
|
113
|
+
(console as any)[level] = (...args: any[]) => {
|
|
114
|
+
try {
|
|
115
|
+
const msg = args
|
|
116
|
+
.map((a) =>
|
|
117
|
+
typeof a === 'string' ? a : safeStringify(a)
|
|
118
|
+
)
|
|
119
|
+
.join(' ');
|
|
120
|
+
pushLog(level === 'log' ? 'log' : (level as LogKind), msg);
|
|
121
|
+
} catch {
|
|
122
|
+
/* swallow */
|
|
123
|
+
}
|
|
124
|
+
original(...args);
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------
|
|
130
|
+
// axios interceptor
|
|
131
|
+
// ---------------------------------------------------------------------
|
|
132
|
+
let axiosPatched = false;
|
|
133
|
+
|
|
134
|
+
export function installAxiosCapture(axiosInstance: any) {
|
|
135
|
+
if (axiosPatched || !axiosInstance?.interceptors) {return;}
|
|
136
|
+
axiosPatched = true;
|
|
137
|
+
|
|
138
|
+
axiosInstance.interceptors.request.use(
|
|
139
|
+
(config: any) => {
|
|
140
|
+
const method = (config.method || 'get').toUpperCase();
|
|
141
|
+
const url = (config.baseURL || '') + (config.url || '');
|
|
142
|
+
pushLog('http', `→ ${method} ${url}`, {
|
|
143
|
+
headers: redactHeaders(config.headers),
|
|
144
|
+
data: config.data,
|
|
145
|
+
});
|
|
146
|
+
return config;
|
|
147
|
+
},
|
|
148
|
+
(error: any) => {
|
|
149
|
+
pushLog('http', '→ request error', error?.message || String(error));
|
|
150
|
+
return Promise.reject(error);
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
axiosInstance.interceptors.response.use(
|
|
155
|
+
(response: any) => {
|
|
156
|
+
const method = (response.config?.method || 'get').toUpperCase();
|
|
157
|
+
const url = (response.config?.baseURL || '') + (response.config?.url || '');
|
|
158
|
+
pushLog(
|
|
159
|
+
'http',
|
|
160
|
+
`← ${response.status} ${method} ${url}`,
|
|
161
|
+
truncateBody(response.data)
|
|
162
|
+
);
|
|
163
|
+
return response;
|
|
164
|
+
},
|
|
165
|
+
(error: any) => {
|
|
166
|
+
const method = (error.config?.method || '?').toUpperCase();
|
|
167
|
+
const url = (error.config?.baseURL || '') + (error.config?.url || '');
|
|
168
|
+
const status = error.response?.status || 'no-status';
|
|
169
|
+
pushLog('http', `← ERR ${status} ${method} ${url}`, {
|
|
170
|
+
data: error.response?.data,
|
|
171
|
+
message: error.message,
|
|
172
|
+
});
|
|
173
|
+
return Promise.reject(error);
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function redactHeaders(headers: any) {
|
|
179
|
+
if (!headers || typeof headers !== 'object') {return headers;}
|
|
180
|
+
const out: any = {};
|
|
181
|
+
for (const k of Object.keys(headers)) {
|
|
182
|
+
const v = headers[k];
|
|
183
|
+
if (/^(authorization|x-custom-token|cookie)$/i.test(k)) {
|
|
184
|
+
out[k] = typeof v === 'string' ? v.slice(0, 16) + '…' : '[redacted]';
|
|
185
|
+
} else {
|
|
186
|
+
out[k] = v;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function truncateBody(body: any) {
|
|
193
|
+
if (body == null) {return body;}
|
|
194
|
+
const str = typeof body === 'string' ? body : safeStringify(body);
|
|
195
|
+
if (str.length <= 2000) {return str;}
|
|
196
|
+
return str.slice(0, 2000) + `… [+${str.length - 2000} chars]`;
|
|
197
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// RN port of web messageNotificationManager — no DOM, queues pending
|
|
2
|
+
// notifications until a callback subscriber registers, dedupes within a
|
|
3
|
+
// short window.
|
|
4
|
+
import { IMessage } from '../types/types';
|
|
5
|
+
|
|
6
|
+
export interface MessageNotificationCallback {
|
|
7
|
+
(
|
|
8
|
+
message: IMessage,
|
|
9
|
+
roomName: string,
|
|
10
|
+
senderName: string,
|
|
11
|
+
roomJID: string
|
|
12
|
+
): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface PendingNotification {
|
|
16
|
+
message: IMessage;
|
|
17
|
+
roomName: string;
|
|
18
|
+
senderName: string;
|
|
19
|
+
roomJID: string;
|
|
20
|
+
ts: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class MessageNotificationManager {
|
|
24
|
+
private callbacks: Set<MessageNotificationCallback> = new Set();
|
|
25
|
+
private pending: PendingNotification[] = [];
|
|
26
|
+
private maxPending = 50;
|
|
27
|
+
private pendingTtlMs = 30_000;
|
|
28
|
+
private dedupeWindowMs = 5000;
|
|
29
|
+
private recentlyDelivered = new Map<string, number>();
|
|
30
|
+
|
|
31
|
+
addCallback(callback: MessageNotificationCallback): () => void {
|
|
32
|
+
this.callbacks.add(callback);
|
|
33
|
+
this.flushPending();
|
|
34
|
+
return () => {
|
|
35
|
+
this.callbacks.delete(callback);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getCallbackCount() {
|
|
40
|
+
return this.callbacks.size;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private flushPending() {
|
|
44
|
+
if (this.callbacks.size === 0 || this.pending.length === 0) {return;}
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const valid = this.pending.filter(
|
|
47
|
+
(item) => now - item.ts <= this.pendingTtlMs
|
|
48
|
+
);
|
|
49
|
+
this.pending = [];
|
|
50
|
+
valid.forEach((item) => {
|
|
51
|
+
this.callbacks.forEach((cb) =>
|
|
52
|
+
cb(item.message, item.roomName, item.senderName, item.roomJID)
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private buildDedupeKey(message: IMessage, roomJID: string): string | null {
|
|
58
|
+
const msgId = String((message as any)?.xmppId || message?.id || '').trim();
|
|
59
|
+
if (msgId) {return `${roomJID}:${msgId}`;}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private shouldSkipDuplicate(message: IMessage, roomJID: string): boolean {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const key = this.buildDedupeKey(message, roomJID);
|
|
66
|
+
if (!key) {return false;}
|
|
67
|
+
const prev = this.recentlyDelivered.get(key) || 0;
|
|
68
|
+
if (prev && now - prev < this.dedupeWindowMs) {return true;}
|
|
69
|
+
this.recentlyDelivered.set(key, now);
|
|
70
|
+
if (this.recentlyDelivered.size > 500) {
|
|
71
|
+
const threshold = now - this.dedupeWindowMs;
|
|
72
|
+
this.recentlyDelivered.forEach((ts, k) => {
|
|
73
|
+
if (ts < threshold) {this.recentlyDelivered.delete(k);}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
showNotification(
|
|
80
|
+
message: IMessage,
|
|
81
|
+
roomName: string,
|
|
82
|
+
senderName: string,
|
|
83
|
+
roomJID: string
|
|
84
|
+
) {
|
|
85
|
+
const body = typeof message?.body === 'string' ? message.body.trim() : '';
|
|
86
|
+
if (!body) {return;}
|
|
87
|
+
if (this.shouldSkipDuplicate(message, roomJID)) {return;}
|
|
88
|
+
|
|
89
|
+
if (this.callbacks.size > 0) {
|
|
90
|
+
this.callbacks.forEach((cb) =>
|
|
91
|
+
cb(message, roomName, senderName, roomJID)
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
this.pending.push({
|
|
95
|
+
message,
|
|
96
|
+
roomName,
|
|
97
|
+
senderName,
|
|
98
|
+
roomJID,
|
|
99
|
+
ts: Date.now(),
|
|
100
|
+
});
|
|
101
|
+
if (this.pending.length > this.maxPending) {
|
|
102
|
+
this.pending = this.pending.slice(-this.maxPending);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const messageNotificationManager = new MessageNotificationManager();
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { requireXmppClient } from './clientRegistry';
|
|
2
|
+
import { store } from '../roomStore';
|
|
3
|
+
import { addRoomMessage } from '../roomStore/roomsSlice';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import { IMessage } from '../types/types';
|
|
6
|
+
|
|
7
|
+
type ResendOptions = {
|
|
8
|
+
respectTranslateConfig?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export async function resendMessage(
|
|
12
|
+
message: Pick<IMessage, 'body' | 'roomJid'> & {
|
|
13
|
+
isReply?: boolean;
|
|
14
|
+
showInChannel?: string;
|
|
15
|
+
mainMessage?: string;
|
|
16
|
+
},
|
|
17
|
+
options: ResendOptions = {}
|
|
18
|
+
): Promise<string> {
|
|
19
|
+
const client = requireXmppClient();
|
|
20
|
+
const state = store.getState();
|
|
21
|
+
const user = state.chatSettingStore.user;
|
|
22
|
+
const config = state.chatSettingStore.config;
|
|
23
|
+
const activeRoomJID = message.roomJid;
|
|
24
|
+
|
|
25
|
+
const id = `resend-text-message-${uuidv4()}`;
|
|
26
|
+
|
|
27
|
+
store.dispatch(
|
|
28
|
+
addRoomMessage({
|
|
29
|
+
roomJID: activeRoomJID,
|
|
30
|
+
message: {
|
|
31
|
+
id,
|
|
32
|
+
user: {
|
|
33
|
+
...user,
|
|
34
|
+
id: user.xmppUsername,
|
|
35
|
+
name: user.firstName + ' ' + user.lastName,
|
|
36
|
+
},
|
|
37
|
+
date: new Date().toISOString(),
|
|
38
|
+
body: message.body,
|
|
39
|
+
roomJid: activeRoomJID,
|
|
40
|
+
xmppFrom: `${activeRoomJID}/${user.xmppUsername}`,
|
|
41
|
+
pending: true,
|
|
42
|
+
isReply: message.isReply || false,
|
|
43
|
+
showInChannel: (message.showInChannel as any) || ('false' as any),
|
|
44
|
+
mainMessage: message.mainMessage || '',
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const useTranslate = !!(
|
|
50
|
+
options.respectTranslateConfig && config?.translates?.enabled
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (useTranslate) {
|
|
54
|
+
client.sendTextMessageWithTranslateTagStanza(
|
|
55
|
+
activeRoomJID,
|
|
56
|
+
user.firstName,
|
|
57
|
+
user.lastName,
|
|
58
|
+
'',
|
|
59
|
+
user.walletAddress,
|
|
60
|
+
message.body,
|
|
61
|
+
'',
|
|
62
|
+
message.isReply || false,
|
|
63
|
+
message.showInChannel === 'true' || false,
|
|
64
|
+
message.mainMessage || '',
|
|
65
|
+
(state.chatSettingStore.langSource as any) || 'en',
|
|
66
|
+
id
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
client.sendMessage(
|
|
70
|
+
activeRoomJID,
|
|
71
|
+
user.firstName,
|
|
72
|
+
user.lastName,
|
|
73
|
+
'',
|
|
74
|
+
user.walletAddress,
|
|
75
|
+
message.body,
|
|
76
|
+
'',
|
|
77
|
+
message.isReply || false,
|
|
78
|
+
message.showInChannel === 'true' || false,
|
|
79
|
+
message.mainMessage || '',
|
|
80
|
+
id
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return id;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default resendMessage;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny pub/sub used by `ToastContext` so non-React code can fire a
|
|
3
|
+
* toast. Replaces an out-of-tree port artifact (`../../../../utils/...`).
|
|
4
|
+
*/
|
|
5
|
+
import type { ToastType } from '../components/Toast';
|
|
6
|
+
|
|
7
|
+
type Listener = (toast: ToastType) => void;
|
|
8
|
+
|
|
9
|
+
const listeners = new Set<Listener>();
|
|
10
|
+
|
|
11
|
+
export const toastEmitter = {
|
|
12
|
+
subscribe(fn: Listener) {
|
|
13
|
+
listeners.add(fn);
|
|
14
|
+
return () => {
|
|
15
|
+
listeners.delete(fn);
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
emit(toast: ToastType) {
|
|
19
|
+
listeners.forEach((fn) => {
|
|
20
|
+
try {
|
|
21
|
+
fn(toast);
|
|
22
|
+
} catch {
|
|
23
|
+
/* swallow */
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { XmppListenerManager } from '../networking/xmpp/listenerManager';
|
|
2
|
+
|
|
3
|
+
export class XmppMemoryMonitor {
|
|
4
|
+
private static monitoringInterval: NodeJS.Timeout | null = null;
|
|
5
|
+
private static warningThreshold = 10;
|
|
6
|
+
private static criticalThreshold = 20;
|
|
7
|
+
|
|
8
|
+
static startMonitoring(intervalMs: number = 5000): void {
|
|
9
|
+
if (this.monitoringInterval) {
|
|
10
|
+
this.stopMonitoring();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this.monitoringInterval = setInterval(() => {
|
|
14
|
+
const count = XmppListenerManager.getActiveListenerCount();
|
|
15
|
+
|
|
16
|
+
if (count >= this.criticalThreshold) {
|
|
17
|
+
console.warn(
|
|
18
|
+
`[XmppMemoryMonitor] CRITICAL: ${count} active XMPP listeners detected!`
|
|
19
|
+
);
|
|
20
|
+
XmppListenerManager.logActiveListeners();
|
|
21
|
+
} else if (count >= this.warningThreshold) {
|
|
22
|
+
console.warn(
|
|
23
|
+
`[XmppMemoryMonitor] WARNING: ${count} active XMPP listeners detected`
|
|
24
|
+
);
|
|
25
|
+
} else if (process.env.NODE_ENV === 'development') {
|
|
26
|
+
console.log(`[XmppMemoryMonitor] Active XMPP listeners: ${count}`);
|
|
27
|
+
}
|
|
28
|
+
}, intervalMs);
|
|
29
|
+
|
|
30
|
+
console.log('[XmppMemoryMonitor] Started monitoring XMPP listeners');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static stopMonitoring(): void {
|
|
34
|
+
if (this.monitoringInterval) {
|
|
35
|
+
clearInterval(this.monitoringInterval);
|
|
36
|
+
this.monitoringInterval = null;
|
|
37
|
+
console.log('[XmppMemoryMonitor] Stopped monitoring XMPP listeners');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static setThresholds(warning: number, critical: number): void {
|
|
42
|
+
this.warningThreshold = warning;
|
|
43
|
+
this.criticalThreshold = critical;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static getCurrentCount(): number {
|
|
47
|
+
return XmppListenerManager.getActiveListenerCount();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static forceCleanup(): void {
|
|
51
|
+
console.warn('[XmppMemoryMonitor] Force cleaning up all XMPP listeners');
|
|
52
|
+
XmppListenerManager.clearAllListeners();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static getListenerInfo(): Array<{
|
|
56
|
+
key: string;
|
|
57
|
+
eventType: string;
|
|
58
|
+
timestamp: number;
|
|
59
|
+
age: number;
|
|
60
|
+
}> {
|
|
61
|
+
return XmppListenerManager.getActiveListenersInfo();
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/version.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ETHORA_CHAT_COMPONENT_VERSION = '25.7.5';
|