@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,148 @@
|
|
|
1
|
+
/** @format */
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState, useRef } from 'react';
|
|
4
|
+
import { AppState } from 'react-native';
|
|
5
|
+
import { useXmppClient } from '../context/xmppProvider';
|
|
6
|
+
import { setBaseURL } from '../networking/apiClient';
|
|
7
|
+
import { loginViaJwt } from '../networking/api-requests/auth.api';
|
|
8
|
+
import { walletToUsername } from '../helpers/walletUsername';
|
|
9
|
+
import { xmppSettingsInterface } from '../types/types';
|
|
10
|
+
import { useAppDispatch } from './hooks';
|
|
11
|
+
import { setUser } from '../roomStore/chatSettingsSlice';
|
|
12
|
+
|
|
13
|
+
interface UseXmppInitializationOptions {
|
|
14
|
+
chatToken?: string | null;
|
|
15
|
+
xmppSettings?: xmppSettingsInterface;
|
|
16
|
+
baseURL?: string;
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface UseXmppInitializationResult {
|
|
21
|
+
isInitializing: boolean;
|
|
22
|
+
initializationError: string | null;
|
|
23
|
+
isInitialized: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Hook for initializing XMPP client via JWT token
|
|
28
|
+
* Used for preloading chat before opening workspace
|
|
29
|
+
*/
|
|
30
|
+
export const useXmppInitialization = (
|
|
31
|
+
options: UseXmppInitializationOptions = {}
|
|
32
|
+
): UseXmppInitializationResult => {
|
|
33
|
+
const {
|
|
34
|
+
chatToken,
|
|
35
|
+
xmppSettings = {
|
|
36
|
+
devServer: 'wss://xmpp.ethoradev.com/ws',
|
|
37
|
+
host: 'xmpp.ethoradev.com',
|
|
38
|
+
conference: 'conference.xmpp.ethoradev.com',
|
|
39
|
+
},
|
|
40
|
+
baseURL = 'https://api.ethoradev.com/v1',
|
|
41
|
+
enabled = true,
|
|
42
|
+
} = options;
|
|
43
|
+
|
|
44
|
+
const { client, initializeClient } = useXmppClient();
|
|
45
|
+
const dispatch = useAppDispatch();
|
|
46
|
+
const [isInitializing, setIsInitializing] = useState(false);
|
|
47
|
+
const [initializationError, setInitializationError] = useState<string | null>(null);
|
|
48
|
+
const isMountedRef = useRef(true);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (baseURL) {
|
|
52
|
+
setBaseURL(baseURL, undefined);
|
|
53
|
+
console.log('🚀 useXmppInitialization: Base URL set for chat', baseURL);
|
|
54
|
+
}
|
|
55
|
+
}, [baseURL]);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!enabled) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const initXmppClient = async () => {
|
|
63
|
+
if (client) {
|
|
64
|
+
console.log('useXmppInitialization: Client already initialized');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!chatToken) {
|
|
69
|
+
console.log('useXmppInitialization: Waiting for chatToken...');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isInitializing) {
|
|
74
|
+
console.log('useXmppInitialization: Initialization in progress...');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (AppState.currentState !== 'active') {
|
|
79
|
+
console.log('useXmppInitialization: App not active, skip init');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
setIsInitializing(true);
|
|
85
|
+
setInitializationError(null);
|
|
86
|
+
console.log('useXmppInitialization: Starting XMPP client initialization...');
|
|
87
|
+
|
|
88
|
+
const user = await loginViaJwt(chatToken);
|
|
89
|
+
console.log('useXmppInitialization: User data received', {
|
|
90
|
+
hasXmppUsername: !!user.xmppUsername,
|
|
91
|
+
hasXmppPassword: !!user.xmppPassword,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
dispatch(setUser(user));
|
|
95
|
+
console.log('useXmppInitialization: User saved to store');
|
|
96
|
+
|
|
97
|
+
const xmppUsername =
|
|
98
|
+
user.xmppUsername ||
|
|
99
|
+
walletToUsername(user.defaultWallet?.walletAddress || '');
|
|
100
|
+
|
|
101
|
+
if (!xmppUsername || !user.xmppPassword) {
|
|
102
|
+
throw new Error('Missing XMPP credentials (username or password)');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log('useXmppInitialization: Initializing XMPP client...');
|
|
106
|
+
const initializedClient = await initializeClient(
|
|
107
|
+
xmppUsername,
|
|
108
|
+
user.xmppPassword,
|
|
109
|
+
xmppSettings
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
console.log('useXmppInitialization: XMPP client initialized successfully', {
|
|
113
|
+
status: initializedClient.status,
|
|
114
|
+
});
|
|
115
|
+
} catch (error: any) {
|
|
116
|
+
console.error('useXmppInitialization: Failed to initialize XMPP client', error);
|
|
117
|
+
if (isMountedRef.current) {
|
|
118
|
+
setInitializationError(error?.message || 'Unknown error');
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
if (isMountedRef.current) {
|
|
122
|
+
setIsInitializing(false);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
initXmppClient();
|
|
128
|
+
}, [
|
|
129
|
+
chatToken,
|
|
130
|
+
client,
|
|
131
|
+
initializeClient,
|
|
132
|
+
isInitializing,
|
|
133
|
+
enabled,
|
|
134
|
+
xmppSettings,
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
return () => {
|
|
139
|
+
isMountedRef.current = false;
|
|
140
|
+
};
|
|
141
|
+
}, []);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
isInitializing,
|
|
145
|
+
initializationError,
|
|
146
|
+
isInitialized: !!client && client.status === 'online',
|
|
147
|
+
};
|
|
148
|
+
};
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library entry point.
|
|
3
|
+
*
|
|
4
|
+
* Consumers drop the component into their own RN/Expo app like:
|
|
5
|
+
*
|
|
6
|
+
* import { Chat } from '@ethora/chat-component';
|
|
7
|
+
*
|
|
8
|
+
* <Chat
|
|
9
|
+
* config={{
|
|
10
|
+
* baseUrl: 'https://api.chat.ethora.com/v1',
|
|
11
|
+
* xmppSettings: { host: 'xmpp.chat.ethora.com' },
|
|
12
|
+
* jwtLogin: { enabled: true, token: '<paste JWT>' },
|
|
13
|
+
* initBeforeLoad: true,
|
|
14
|
+
* colors: { primary: '#5E3FDE' },
|
|
15
|
+
* }}
|
|
16
|
+
* />
|
|
17
|
+
*
|
|
18
|
+
* The provider wires up redux + xmpp + toast automatically. Pass
|
|
19
|
+
* `roomJID` to lock the chat to a single room, or omit it to show the
|
|
20
|
+
* built-in room list.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import 'react-native-get-random-values';
|
|
24
|
+
|
|
25
|
+
// Main mount points.
|
|
26
|
+
export { ReduxWrapper as Chat } from './components/MainComponents/ReduxWrapper';
|
|
27
|
+
export { XmppProvider } from './context/xmppProvider';
|
|
28
|
+
|
|
29
|
+
// Hooks that consumers usually need (badge counts, logout, push, etc.).
|
|
30
|
+
export { useUnread } from './hooks/useUnreadMessagesCounter';
|
|
31
|
+
export { logoutService } from './hooks/useLogout';
|
|
32
|
+
export { useQRCodeChat, handleQRChatId } from './hooks/useQRCodeChatHandler';
|
|
33
|
+
export { resendMessage } from './utils/resendMessage';
|
|
34
|
+
|
|
35
|
+
// Public types — needed so consumers can type their `config` prop and
|
|
36
|
+
// any event handlers they pass in.
|
|
37
|
+
export type {
|
|
38
|
+
IConfig,
|
|
39
|
+
IRoom,
|
|
40
|
+
IMessage,
|
|
41
|
+
IUser,
|
|
42
|
+
User,
|
|
43
|
+
ConfigUser,
|
|
44
|
+
MessageProps,
|
|
45
|
+
} from './types/types';
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { User } from '../../types/types';
|
|
2
|
+
|
|
3
|
+
// import {
|
|
4
|
+
// getAuth,
|
|
5
|
+
// GoogleAuthProvider,
|
|
6
|
+
// signInWithPopup,
|
|
7
|
+
// User as FirebaseUser,
|
|
8
|
+
// } from 'firebase/auth';
|
|
9
|
+
// import { app } from '../../../firebase-config';
|
|
10
|
+
|
|
11
|
+
import http, { appToken } from '../apiClient';
|
|
12
|
+
import { store } from '../../roomStore';
|
|
13
|
+
|
|
14
|
+
// login functions
|
|
15
|
+
export async function loginEmail(email: string, password: string) {
|
|
16
|
+
const res = await http.post<{
|
|
17
|
+
user: User;
|
|
18
|
+
refreshToken: string;
|
|
19
|
+
token: string;
|
|
20
|
+
}>(
|
|
21
|
+
'/users/login-with-email',
|
|
22
|
+
{
|
|
23
|
+
email,
|
|
24
|
+
password,
|
|
25
|
+
},
|
|
26
|
+
{ headers: { Authorization: appToken } }
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
console.log('loginEmail res', res.data);
|
|
30
|
+
|
|
31
|
+
return res;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function loginSocial(
|
|
35
|
+
idToken: string,
|
|
36
|
+
accessToken: string,
|
|
37
|
+
loginType: string,
|
|
38
|
+
authToken: string = 'authToken'
|
|
39
|
+
) {
|
|
40
|
+
return http.post<any>(
|
|
41
|
+
'/users/login',
|
|
42
|
+
{
|
|
43
|
+
idToken,
|
|
44
|
+
accessToken,
|
|
45
|
+
loginType,
|
|
46
|
+
authToken,
|
|
47
|
+
},
|
|
48
|
+
{ headers: { Authorization: appToken } }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function registerSocial(
|
|
53
|
+
idToken: string,
|
|
54
|
+
accessToken: string,
|
|
55
|
+
authToken: string,
|
|
56
|
+
loginType: string,
|
|
57
|
+
signUpPlan?: string
|
|
58
|
+
) {
|
|
59
|
+
return http.post(
|
|
60
|
+
'/users',
|
|
61
|
+
{
|
|
62
|
+
idToken,
|
|
63
|
+
accessToken,
|
|
64
|
+
loginType,
|
|
65
|
+
authToken: authToken,
|
|
66
|
+
signupPlan: signUpPlan,
|
|
67
|
+
},
|
|
68
|
+
{ headers: { Authorization: appToken } }
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function checkEmailExist(email: string) {
|
|
73
|
+
return http.get(
|
|
74
|
+
'/users/checkEmail/' + email,
|
|
75
|
+
|
|
76
|
+
{ headers: { Authorization: appToken } }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function loginViaJwt(clientToken: string): Promise<User> {
|
|
81
|
+
console.log('🟡 loginViaJwt: Sending JWT request to /users/client');
|
|
82
|
+
const response = await http.post<{
|
|
83
|
+
user: User;
|
|
84
|
+
refreshToken: string;
|
|
85
|
+
token: string;
|
|
86
|
+
}>('/users/client', null, { headers: { 'x-custom-token': clientToken } });
|
|
87
|
+
|
|
88
|
+
console.log('✅ loginViaJwt: Received response', {
|
|
89
|
+
hasUser: !!response.data.user,
|
|
90
|
+
hasToken: !!response.data.token,
|
|
91
|
+
hasRefreshToken: !!response.data.refreshToken,
|
|
92
|
+
userData: response.data.user ? {
|
|
93
|
+
_id: response.data.user._id,
|
|
94
|
+
email: response.data.user.email,
|
|
95
|
+
firstName: response.data.user.firstName,
|
|
96
|
+
lastName: response.data.user.lastName,
|
|
97
|
+
xmppUsername: response.data.user.xmppUsername,
|
|
98
|
+
} : null,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const user = {
|
|
102
|
+
...response.data.user,
|
|
103
|
+
refreshToken: response.data.refreshToken,
|
|
104
|
+
token: response.data.token,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return user;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// export const signInWithGoogle = async () => {
|
|
111
|
+
// const auth = getAuth(app);
|
|
112
|
+
// const googleProvider = new GoogleAuthProvider();
|
|
113
|
+
// googleProvider.addScope('https://www.googleapis.com/auth/userinfo.email');
|
|
114
|
+
// googleProvider.addScope('https://www.googleapis.com/auth/userinfo.profile');
|
|
115
|
+
// try {
|
|
116
|
+
// const res = await signInWithPopup(auth, googleProvider);
|
|
117
|
+
// const user = res.user as FirebaseUser;
|
|
118
|
+
// const idToken = await auth?.currentUser?.getIdToken();
|
|
119
|
+
// const credential = GoogleAuthProvider.credentialFromResult(res);
|
|
120
|
+
// return {
|
|
121
|
+
// user,
|
|
122
|
+
// idToken,
|
|
123
|
+
// credential,
|
|
124
|
+
// };
|
|
125
|
+
// } catch (error) {
|
|
126
|
+
// console.error(error);
|
|
127
|
+
// return {};
|
|
128
|
+
// }
|
|
129
|
+
// };
|
|
130
|
+
|
|
131
|
+
export function uploadFile(formData: FormData) {
|
|
132
|
+
const token = store.getState().chatSettingStore?.user?.token ?? '';
|
|
133
|
+
return http.post('/files/', formData, {
|
|
134
|
+
headers: {
|
|
135
|
+
Authorization: token,
|
|
136
|
+
Accept: '*/*',
|
|
137
|
+
// "Content-Type": "multipart/form-data",
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import { store } from '../../roomStore';
|
|
4
|
+
|
|
5
|
+
const PUSH_API_URL = 'https://push.ethoradev.com/api/v1';
|
|
6
|
+
|
|
7
|
+
const pushAxios = axios.create({
|
|
8
|
+
baseURL: PUSH_API_URL,
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export interface PushSubscriptionPayload {
|
|
15
|
+
projectId: string;
|
|
16
|
+
registrationToken: string;
|
|
17
|
+
deviceType: 'web' | 'android' | 'ios';
|
|
18
|
+
jid: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function subscribeToPushNotifications(
|
|
22
|
+
fcmToken: string,
|
|
23
|
+
userJid: string,
|
|
24
|
+
projectName: string = 'Preshent',
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
const deviceType: 'web' | 'android' | 'ios' = Platform.select({
|
|
27
|
+
ios: 'ios',
|
|
28
|
+
android: 'android',
|
|
29
|
+
default: 'web',
|
|
30
|
+
}) as 'web' | 'android' | 'ios';
|
|
31
|
+
|
|
32
|
+
const payload: PushSubscriptionPayload = {
|
|
33
|
+
projectId: projectName,
|
|
34
|
+
registrationToken: fcmToken,
|
|
35
|
+
deviceType,
|
|
36
|
+
jid: `${userJid}@xmpp.ethoradev.com`,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
console.log('payload', payload);
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const token = store.getState().chatSettingStore?.user?.token || '';
|
|
43
|
+
|
|
44
|
+
const response = await pushAxios.post('/subscriptions', payload, {
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: `Bearer ${token}`,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
console.log('Successfully subscribed to push notifications', response.data);
|
|
50
|
+
return response.data;
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
console.error('Failed to subscribe to push notifications:', error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import http from '../apiClient';
|
|
2
|
+
import { RoomMember } from '../../types/types';
|
|
3
|
+
|
|
4
|
+
export async function getUserByXmppUsername(
|
|
5
|
+
xmppUsername: string,
|
|
6
|
+
token: string
|
|
7
|
+
): Promise<RoomMember | null> {
|
|
8
|
+
try {
|
|
9
|
+
const res = await http.get<{ result: RoomMember }>(
|
|
10
|
+
`/apps/users/${xmppUsername}`,
|
|
11
|
+
{
|
|
12
|
+
headers: { Authorization: token },
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
return res.data.result;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.error(`Failed to fetch user: ${xmppUsername}`, error);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { store } from '../../roomStore';
|
|
2
|
+
import { addRoom } from '../../roomStore/roomsSlice';
|
|
3
|
+
import { IRoom } from '../../types/types';
|
|
4
|
+
import http from '../apiClient';
|
|
5
|
+
|
|
6
|
+
interface ApiRoomMember {
|
|
7
|
+
_id: string;
|
|
8
|
+
firstName?: string;
|
|
9
|
+
lastName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ApiRoom {
|
|
13
|
+
_id?: string;
|
|
14
|
+
name: string;
|
|
15
|
+
title?: string;
|
|
16
|
+
jid?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
participants?: number;
|
|
19
|
+
type?: string;
|
|
20
|
+
icon?: string;
|
|
21
|
+
picture?: string;
|
|
22
|
+
members?: ApiRoomMember[];
|
|
23
|
+
createdAt?: string;
|
|
24
|
+
updatedAt?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Materialize a REST `/chats/my` item into an `IRoom` + dispatch
|
|
29
|
+
* `addRoom`. Without this, REST-fetched rooms never make it into
|
|
30
|
+
* `state.rooms.rooms` and `ChatRoom` falls back to the "no rooms,
|
|
31
|
+
* create one!" empty state even when the user has rooms server-side.
|
|
32
|
+
*
|
|
33
|
+
* The room jid is derived from the conference host. We try several
|
|
34
|
+
* places:
|
|
35
|
+
* 1. explicit `room.jid` from the server (if present)
|
|
36
|
+
* 2. `<name>@conference.<xmppHost>` where xmppHost comes from the
|
|
37
|
+
* provider's `xmppSettings.host` saved to `chatSettingStore.config`
|
|
38
|
+
* 3. fallback: `<name>@conference.xmpp.chat.ethora.com`
|
|
39
|
+
*/
|
|
40
|
+
function dispatchRoomsFromRestItems(items: ApiRoom[]): void {
|
|
41
|
+
if (!items?.length) return;
|
|
42
|
+
const config = store.getState().chatSettingStore?.config as any;
|
|
43
|
+
const host: string =
|
|
44
|
+
config?.xmppSettings?.host ||
|
|
45
|
+
config?.xmppSettings?.conference?.replace(/^conference\./, '') ||
|
|
46
|
+
'xmpp.chat.ethora.com';
|
|
47
|
+
const conference =
|
|
48
|
+
config?.xmppSettings?.conference || `conference.${host}`;
|
|
49
|
+
|
|
50
|
+
for (const item of items) {
|
|
51
|
+
if (!item) continue;
|
|
52
|
+
const jid = item.jid || `${item.name}@${conference}`;
|
|
53
|
+
if (!jid.includes('@')) continue;
|
|
54
|
+
const room: IRoom = {
|
|
55
|
+
id: item._id || jid,
|
|
56
|
+
jid,
|
|
57
|
+
name: item.name,
|
|
58
|
+
title: item.title || item.name,
|
|
59
|
+
usersCnt: item.participants ?? item.members?.length ?? 0,
|
|
60
|
+
messages: [],
|
|
61
|
+
isLoading: false,
|
|
62
|
+
roomBg: '',
|
|
63
|
+
icon: item.picture || item.icon,
|
|
64
|
+
createdAt: item.createdAt,
|
|
65
|
+
updatedAt: item.updatedAt,
|
|
66
|
+
description: (item as any).description,
|
|
67
|
+
type: (item as any).type,
|
|
68
|
+
// ChatProfileModal renders this list under the description/type
|
|
69
|
+
// fields. The REST API returns `members` (an array of {_id,
|
|
70
|
+
// firstName?, lastName?}); map to the shape RoomMember expects.
|
|
71
|
+
roomMembers: (item.members || []).map((m: any) => ({
|
|
72
|
+
firstName: m.firstName,
|
|
73
|
+
lastName: m.lastName,
|
|
74
|
+
xmppUsername: m._id || '',
|
|
75
|
+
role: m.role,
|
|
76
|
+
ban_status: m.ban_status,
|
|
77
|
+
last_active: m.last_active,
|
|
78
|
+
jid: m.jid || '',
|
|
79
|
+
})) as any,
|
|
80
|
+
};
|
|
81
|
+
store.dispatch(addRoom({ roomData: room }));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const GET_ROOMS_CACHE_MS = 60_000;
|
|
86
|
+
let getRoomsInFlight: Promise<{ items: ApiRoom[] }> | null = null;
|
|
87
|
+
let getRoomsInFlightToken = '';
|
|
88
|
+
let lastGetRoomsResponse: { items: ApiRoom[] } | null = null;
|
|
89
|
+
let lastGetRoomsResponseAt = 0;
|
|
90
|
+
let lastGetRoomsResponseToken = '';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* REST equivalent of getRoomsStanza. Used by initBeforeLoad to prefetch
|
|
94
|
+
* the room list in parallel with the XMPP handshake. Results are cached
|
|
95
|
+
* per-token for 60s so a downstream re-fetch on mount is a no-op.
|
|
96
|
+
*/
|
|
97
|
+
export async function getRooms(): Promise<{ items: ApiRoom[] }> {
|
|
98
|
+
const token = store.getState().chatSettingStore.user.token || '';
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
lastGetRoomsResponse &&
|
|
103
|
+
lastGetRoomsResponseToken === token &&
|
|
104
|
+
now - lastGetRoomsResponseAt < GET_ROOMS_CACHE_MS
|
|
105
|
+
) {
|
|
106
|
+
return lastGetRoomsResponse;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (getRoomsInFlight && getRoomsInFlightToken === token) {
|
|
110
|
+
return getRoomsInFlight;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getRoomsInFlightToken = token;
|
|
114
|
+
getRoomsInFlight = (async () => {
|
|
115
|
+
const response = await http.get('/chats/my', {
|
|
116
|
+
headers: { Authorization: token },
|
|
117
|
+
});
|
|
118
|
+
lastGetRoomsResponse = response.data;
|
|
119
|
+
lastGetRoomsResponseAt = Date.now();
|
|
120
|
+
lastGetRoomsResponseToken = token;
|
|
121
|
+
dispatchRoomsFromRestItems(response.data?.items || []);
|
|
122
|
+
return response.data;
|
|
123
|
+
})();
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
return await getRoomsInFlight;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.log('Error loading rooms via REST', error);
|
|
129
|
+
return { items: [] };
|
|
130
|
+
} finally {
|
|
131
|
+
getRoomsInFlight = null;
|
|
132
|
+
getRoomsInFlightToken = '';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function clearRoomsRestCache() {
|
|
137
|
+
lastGetRoomsResponse = null;
|
|
138
|
+
lastGetRoomsResponseAt = 0;
|
|
139
|
+
lastGetRoomsResponseToken = '';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type { ApiRoom };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import http from '../apiClient';
|
|
2
|
+
import { store } from '../../roomStore';
|
|
3
|
+
import { User } from '../../types/types';
|
|
4
|
+
|
|
5
|
+
interface GetMyUserOptions {
|
|
6
|
+
token?: string;
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getMyUser(options?: GetMyUserOptions): Promise<User> {
|
|
11
|
+
const token =
|
|
12
|
+
options?.token || store.getState().chatSettingStore.user.token || '';
|
|
13
|
+
const endpoint = options?.endpoint || '/users/my';
|
|
14
|
+
|
|
15
|
+
const response = await http.get(endpoint, {
|
|
16
|
+
headers: { Authorization: token },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (response?.data?.user) {
|
|
20
|
+
return response.data.user as User;
|
|
21
|
+
}
|
|
22
|
+
return response.data as User;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getDocuments(walletAddress: string) {
|
|
26
|
+
const token = store.getState().chatSettingStore.user.token || '';
|
|
27
|
+
return http.get(`/docs/${walletAddress}`, {
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${token}`,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getExportMyData() {
|
|
35
|
+
const token = store.getState().chatSettingStore.user.token || '';
|
|
36
|
+
return http.get('/users/exportData', {
|
|
37
|
+
headers: {
|
|
38
|
+
Authorization: token,
|
|
39
|
+
responseType: 'arraybuffer',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function deleteMe() {
|
|
45
|
+
return http.delete('/users');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function updateMe(data: any) {
|
|
49
|
+
return http.put('/users', data);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function updateProfile(fd: FormData): Promise<{ user: User }> {
|
|
53
|
+
const token = store.getState().chatSettingStore.user.token || '';
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const response = await http.put('/users', fd, {
|
|
57
|
+
headers: {
|
|
58
|
+
Authorization: token,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
return response.data;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new Error('Error updating profile');
|
|
64
|
+
}
|
|
65
|
+
}
|