@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,353 @@
|
|
|
1
|
+
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
+
import { EditAction, HistoryPreloadState, IMessage, IRoom } from '../types/types';
|
|
3
|
+
import { insertMessageWithDelimiter } from '../helpers/insertMessageWithDelimiter';
|
|
4
|
+
|
|
5
|
+
export interface RoomPreloadPatch {
|
|
6
|
+
jid: string;
|
|
7
|
+
messages?: IMessage[];
|
|
8
|
+
historyPreloadState?: HistoryPreloadState;
|
|
9
|
+
unreadCapped?: boolean;
|
|
10
|
+
historyComplete?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface RoomMessagesState {
|
|
14
|
+
rooms: { [jid: string]: IRoom };
|
|
15
|
+
activeRoomJID: string | null;
|
|
16
|
+
editAction?: EditAction;
|
|
17
|
+
isLoading: boolean;
|
|
18
|
+
pendingNotificationJid?: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const initialState: RoomMessagesState = {
|
|
22
|
+
rooms: {},
|
|
23
|
+
activeRoomJID: null,
|
|
24
|
+
isLoading: false,
|
|
25
|
+
editAction: {
|
|
26
|
+
isEdit: false,
|
|
27
|
+
roomJid: '',
|
|
28
|
+
messageId: '',
|
|
29
|
+
text: '',
|
|
30
|
+
},
|
|
31
|
+
pendingNotificationJid: null,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const roomsStore = createSlice({
|
|
35
|
+
name: 'roomMessages',
|
|
36
|
+
initialState,
|
|
37
|
+
reducers: {
|
|
38
|
+
addRoom(state, action: PayloadAction<{ roomData: IRoom }>) {
|
|
39
|
+
const { roomData } = action.payload;
|
|
40
|
+
state.rooms[roomData.jid] = roomData;
|
|
41
|
+
},
|
|
42
|
+
deleteRoom(state, action: PayloadAction<{ jid: string }>) {
|
|
43
|
+
const { jid } = action.payload;
|
|
44
|
+
if (state.rooms[jid]) {
|
|
45
|
+
delete state.rooms[jid];
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
updateRoom(
|
|
49
|
+
state,
|
|
50
|
+
action: PayloadAction<{ jid: string; updates: Partial<IRoom> }>
|
|
51
|
+
) {
|
|
52
|
+
const { jid, updates } = action.payload;
|
|
53
|
+
const existingRoom = state.rooms[jid];
|
|
54
|
+
|
|
55
|
+
if (existingRoom) {
|
|
56
|
+
state.rooms[jid] = {
|
|
57
|
+
...existingRoom,
|
|
58
|
+
...updates,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
setRoomMessages(
|
|
63
|
+
state,
|
|
64
|
+
action: PayloadAction<{ roomJID: string; messages: IMessage[] }>
|
|
65
|
+
) {
|
|
66
|
+
const { roomJID, messages } = action.payload;
|
|
67
|
+
if (state.rooms[roomJID]) {
|
|
68
|
+
state.rooms[roomJID].messages = messages;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
deleteRoomMessage(
|
|
72
|
+
state,
|
|
73
|
+
action: PayloadAction<{ roomJID: string; messageId: string }>
|
|
74
|
+
) {
|
|
75
|
+
const { roomJID, messageId } = action.payload;
|
|
76
|
+
if (state.rooms[roomJID]) {
|
|
77
|
+
state.rooms[roomJID].messages.map((message) => {
|
|
78
|
+
if (message.id === messageId) {
|
|
79
|
+
message.isDeleted = true;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
setEditAction: (state, action: PayloadAction<EditAction | undefined>) => {
|
|
85
|
+
if (action.payload?.isEdit) {
|
|
86
|
+
state.editAction = action.payload;
|
|
87
|
+
} else {
|
|
88
|
+
state.editAction = {
|
|
89
|
+
isEdit: false,
|
|
90
|
+
roomJid: '',
|
|
91
|
+
messageId: '',
|
|
92
|
+
text: '',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
editRoomMessage(
|
|
97
|
+
state,
|
|
98
|
+
action: PayloadAction<{
|
|
99
|
+
roomJID: string;
|
|
100
|
+
messageId: string;
|
|
101
|
+
text: string;
|
|
102
|
+
}>
|
|
103
|
+
) {
|
|
104
|
+
const { roomJID, messageId, text } = action.payload;
|
|
105
|
+
if (state.rooms[roomJID]) {
|
|
106
|
+
state.rooms[roomJID].messages.map((message) => {
|
|
107
|
+
if (message.id === messageId) {
|
|
108
|
+
message.body = text;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
addRoomMessage(
|
|
114
|
+
state,
|
|
115
|
+
action: PayloadAction<{
|
|
116
|
+
roomJID: string;
|
|
117
|
+
message: IMessage;
|
|
118
|
+
start?: boolean;
|
|
119
|
+
}>
|
|
120
|
+
) {
|
|
121
|
+
const { roomJID, message, start } = action.payload;
|
|
122
|
+
|
|
123
|
+
if (!state.rooms[roomJID]?.messages) {
|
|
124
|
+
state.rooms[roomJID].messages = [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const roomMessages = state.rooms[roomJID].messages;
|
|
128
|
+
|
|
129
|
+
if (roomMessages.length === 0 || start) {
|
|
130
|
+
roomMessages.unshift(message);
|
|
131
|
+
} else {
|
|
132
|
+
const lastViewedTimestamp = state.rooms[roomJID].lastViewedTimestamp
|
|
133
|
+
? new Date(state.rooms[roomJID].lastViewedTimestamp)
|
|
134
|
+
: null;
|
|
135
|
+
|
|
136
|
+
insertMessageWithDelimiter(roomMessages, message, lastViewedTimestamp);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
deleteAllRooms(state) {
|
|
140
|
+
state.rooms = {};
|
|
141
|
+
},
|
|
142
|
+
setComposing(
|
|
143
|
+
state,
|
|
144
|
+
action: PayloadAction<{
|
|
145
|
+
chatJID: string;
|
|
146
|
+
composing: boolean;
|
|
147
|
+
composingList?: string[];
|
|
148
|
+
}>
|
|
149
|
+
) {
|
|
150
|
+
const { chatJID, composing, composingList } = action.payload;
|
|
151
|
+
state.rooms[chatJID].composing = composing;
|
|
152
|
+
state.rooms[chatJID].composingList = composingList;
|
|
153
|
+
},
|
|
154
|
+
setIsLoading: (
|
|
155
|
+
state,
|
|
156
|
+
action: PayloadAction<{ chatJID?: string; loading: boolean }>
|
|
157
|
+
) => {
|
|
158
|
+
const { chatJID, loading } = action.payload;
|
|
159
|
+
if (chatJID && state.rooms?.[chatJID]) {
|
|
160
|
+
state.rooms[chatJID].isLoading = loading;
|
|
161
|
+
}
|
|
162
|
+
state.isLoading = loading;
|
|
163
|
+
},
|
|
164
|
+
setLastViewedTimestamp: (
|
|
165
|
+
state,
|
|
166
|
+
action: PayloadAction<{ chatJID: string; timestamp: number }>
|
|
167
|
+
) => {
|
|
168
|
+
const { chatJID, timestamp } = action.payload;
|
|
169
|
+
if (state.rooms[chatJID]) {
|
|
170
|
+
state.rooms[chatJID].lastViewedTimestamp = timestamp;
|
|
171
|
+
// timestamp === 0 means "user is currently viewing this room" —
|
|
172
|
+
// unread is cleared. Otherwise count messages received strictly
|
|
173
|
+
// after the last-viewed instant.
|
|
174
|
+
if (!timestamp) {
|
|
175
|
+
state.rooms[chatJID].unreadMessages = 0;
|
|
176
|
+
} else {
|
|
177
|
+
state.rooms[chatJID].unreadMessages = countNewerMessages(
|
|
178
|
+
state.rooms[chatJID].messages,
|
|
179
|
+
timestamp
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
setRoomRole: (
|
|
185
|
+
state,
|
|
186
|
+
action: PayloadAction<{ chatJID: string; role: string }>
|
|
187
|
+
) => {
|
|
188
|
+
const { chatJID, role } = action.payload;
|
|
189
|
+
if (state.rooms[chatJID]) {
|
|
190
|
+
state.rooms[chatJID].role = role;
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
setRoomNoMessages: (
|
|
194
|
+
state,
|
|
195
|
+
action: PayloadAction<{ value: boolean; chatJID?: string }>
|
|
196
|
+
) => {
|
|
197
|
+
const { value, chatJID } = action.payload;
|
|
198
|
+
if (chatJID) {
|
|
199
|
+
state.rooms[chatJID].noMessages = value;
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
setCurrentRoom: (
|
|
203
|
+
state,
|
|
204
|
+
action: PayloadAction<{ roomJID: string | null }>
|
|
205
|
+
) => {
|
|
206
|
+
// Accept null/empty so callers can clear the active room (e.g.
|
|
207
|
+
// back button from chat → return to RoomList). Mirrors web.
|
|
208
|
+
state.activeRoomJID = action.payload.roomJID || '';
|
|
209
|
+
},
|
|
210
|
+
/**
|
|
211
|
+
* Stash a JID that a push notification asked us to open before
|
|
212
|
+
* the rooms list has loaded. The Chat component clears this once
|
|
213
|
+
* the room exists locally and dispatches `setCurrentRoom`.
|
|
214
|
+
*/
|
|
215
|
+
setPendingNotificationJid: (
|
|
216
|
+
state,
|
|
217
|
+
action: PayloadAction<string | null>
|
|
218
|
+
) => {
|
|
219
|
+
state.pendingNotificationJid = action.payload;
|
|
220
|
+
},
|
|
221
|
+
clearPendingNotificationJid: (state) => {
|
|
222
|
+
state.pendingNotificationJid = null;
|
|
223
|
+
},
|
|
224
|
+
/**
|
|
225
|
+
* Stamp a message in `state.rooms[roomJID].messages` with an updated
|
|
226
|
+
* reactions list. The reactionsMiddleware listens for this action to
|
|
227
|
+
* keep `IRoom.lastMessage` / `lastMessageTimestamp` in sync.
|
|
228
|
+
*/
|
|
229
|
+
setReactions: (
|
|
230
|
+
state,
|
|
231
|
+
action: PayloadAction<{
|
|
232
|
+
roomJID: string;
|
|
233
|
+
messageId: string;
|
|
234
|
+
from?: string;
|
|
235
|
+
reactions: string[];
|
|
236
|
+
latestReactionTimestamp?: string;
|
|
237
|
+
data?: Record<string, string>;
|
|
238
|
+
}>
|
|
239
|
+
) => {
|
|
240
|
+
const { roomJID, messageId, reactions } = action.payload;
|
|
241
|
+
const room = state.rooms[roomJID];
|
|
242
|
+
if (!room?.messages) {return;}
|
|
243
|
+
for (const msg of room.messages) {
|
|
244
|
+
if (msg?.id === messageId) {
|
|
245
|
+
(msg as any).reactions = reactions;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
setLogoutState: (state) => {
|
|
251
|
+
state.rooms = {};
|
|
252
|
+
state.activeRoomJID = null;
|
|
253
|
+
state.isLoading = false;
|
|
254
|
+
},
|
|
255
|
+
setActiveMessage: (
|
|
256
|
+
state,
|
|
257
|
+
action: PayloadAction<{ id: string; chatJID: string }>
|
|
258
|
+
) => {
|
|
259
|
+
const { id, chatJID } = action.payload;
|
|
260
|
+
|
|
261
|
+
state.rooms[chatJID].messages.map((message) => {
|
|
262
|
+
if (message.id === id) {
|
|
263
|
+
message.activeMessage = true;
|
|
264
|
+
} else {
|
|
265
|
+
message.activeMessage = false;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
setCloseActiveMessage: (
|
|
270
|
+
state,
|
|
271
|
+
action: PayloadAction<{ chatJID: string }>
|
|
272
|
+
) => {
|
|
273
|
+
const { chatJID } = action.payload;
|
|
274
|
+
|
|
275
|
+
state.rooms[chatJID].messages.map((message) => {
|
|
276
|
+
message.activeMessage = false;
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
/**
|
|
280
|
+
* Batched update used by the history preload scheduler. Each patch is
|
|
281
|
+
* applied to its room: messages REPLACE (when provided), state fields
|
|
282
|
+
* MERGE. Rooms that don't exist yet are ignored.
|
|
283
|
+
*/
|
|
284
|
+
applyRoomsPreloadBatch: (
|
|
285
|
+
state,
|
|
286
|
+
action: PayloadAction<{ rooms: RoomPreloadPatch[] }>
|
|
287
|
+
) => {
|
|
288
|
+
const { rooms } = action.payload;
|
|
289
|
+
for (const patch of rooms) {
|
|
290
|
+
const room = state.rooms[patch.jid];
|
|
291
|
+
if (!room) {continue;}
|
|
292
|
+
if (typeof patch.historyPreloadState !== 'undefined') {
|
|
293
|
+
room.historyPreloadState = patch.historyPreloadState;
|
|
294
|
+
}
|
|
295
|
+
if (typeof patch.unreadCapped !== 'undefined') {
|
|
296
|
+
room.unreadCapped = patch.unreadCapped;
|
|
297
|
+
}
|
|
298
|
+
if (typeof patch.historyComplete !== 'undefined') {
|
|
299
|
+
room.historyComplete = patch.historyComplete;
|
|
300
|
+
}
|
|
301
|
+
if (Array.isArray(patch.messages)) {
|
|
302
|
+
// Merge: keep any locally-pending messages, replace the rest.
|
|
303
|
+
const pending = room.messages?.filter((m) => m?.pending) || [];
|
|
304
|
+
room.messages = [...patch.messages, ...pending];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Count messages strictly newer than the given millisecond timestamp.
|
|
312
|
+
// Uses `msg.date` (canonical ISO/Date) so it matches the unread
|
|
313
|
+
// middleware. Excludes the "delimiter-new" sentinel and pending sends.
|
|
314
|
+
const countNewerMessages = (
|
|
315
|
+
messages: IMessage[],
|
|
316
|
+
timestamp: number
|
|
317
|
+
): number => {
|
|
318
|
+
if (!messages?.length || !timestamp) {return 0;}
|
|
319
|
+
let count = 0;
|
|
320
|
+
for (const message of messages) {
|
|
321
|
+
if (!message || message.id === 'delimiter-new' || message.pending) {continue;}
|
|
322
|
+
const ms = new Date(message.date as any).getTime();
|
|
323
|
+
if (Number.isFinite(ms) && ms > timestamp) {count += 1;}
|
|
324
|
+
}
|
|
325
|
+
return count;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
export const {
|
|
329
|
+
addRoom,
|
|
330
|
+
deleteAllRooms,
|
|
331
|
+
setRoomMessages,
|
|
332
|
+
addRoomMessage,
|
|
333
|
+
deleteRoomMessage,
|
|
334
|
+
setEditAction,
|
|
335
|
+
editRoomMessage,
|
|
336
|
+
setComposing,
|
|
337
|
+
setIsLoading,
|
|
338
|
+
setLastViewedTimestamp,
|
|
339
|
+
setRoomNoMessages,
|
|
340
|
+
setCurrentRoom,
|
|
341
|
+
setRoomRole,
|
|
342
|
+
setLogoutState,
|
|
343
|
+
setActiveMessage,
|
|
344
|
+
setCloseActiveMessage,
|
|
345
|
+
deleteRoom,
|
|
346
|
+
updateRoom,
|
|
347
|
+
applyRoomsPreloadBatch,
|
|
348
|
+
setPendingNotificationJid,
|
|
349
|
+
clearPendingNotificationJid,
|
|
350
|
+
setReactions,
|
|
351
|
+
} = roomsStore.actions;
|
|
352
|
+
|
|
353
|
+
export default roomsStore.reducer;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import messaging, { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
|
|
2
|
+
import { getApp } from '@react-native-firebase/app';
|
|
3
|
+
import { Platform, PermissionsAndroid } from 'react-native';
|
|
4
|
+
|
|
5
|
+
let appInstance: any;
|
|
6
|
+
try {
|
|
7
|
+
appInstance = getApp();
|
|
8
|
+
} catch (error) {
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const AuthorizationStatus = (messaging as any).AuthorizationStatus;
|
|
12
|
+
|
|
13
|
+
let cachedMessagingInstance: any = null;
|
|
14
|
+
|
|
15
|
+
const getMessagingInstance = () => {
|
|
16
|
+
if (cachedMessagingInstance) {
|
|
17
|
+
return cachedMessagingInstance;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!appInstance) {
|
|
21
|
+
try {
|
|
22
|
+
appInstance = getApp();
|
|
23
|
+
} catch (e) {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
cachedMessagingInstance = messaging();
|
|
28
|
+
return cachedMessagingInstance;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export async function requestNotificationPermission(): Promise<boolean> {
|
|
32
|
+
const messagingInstance = getMessagingInstance();
|
|
33
|
+
if (Platform.OS === 'ios') {
|
|
34
|
+
const authStatus = await messagingInstance.requestPermission();
|
|
35
|
+
return (
|
|
36
|
+
authStatus === AuthorizationStatus.AUTHORIZED ||
|
|
37
|
+
authStatus === AuthorizationStatus.PROVISIONAL
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (Platform.OS === 'android' && Platform.Version >= 33) {
|
|
42
|
+
const granted = await PermissionsAndroid.request(
|
|
43
|
+
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
|
|
44
|
+
);
|
|
45
|
+
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function getFCMToken(): Promise<string | null> {
|
|
52
|
+
try {
|
|
53
|
+
const messagingInstance = getMessagingInstance();
|
|
54
|
+
if (!messagingInstance.isDeviceRegisteredForRemoteMessages) {
|
|
55
|
+
await messagingInstance.registerDeviceForRemoteMessages();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (Platform.OS === 'ios') {
|
|
59
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
60
|
+
|
|
61
|
+
let apnsToken = await messagingInstance.getAPNSToken();
|
|
62
|
+
|
|
63
|
+
if (!apnsToken) {
|
|
64
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
65
|
+
apnsToken = await messagingInstance.getAPNSToken();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// No APNS token means push not configured - this is OK for development
|
|
69
|
+
if (!apnsToken) {
|
|
70
|
+
console.log('Push notifications not configured (missing APNs). App will work without push.');
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const token = await messagingInstance.getToken();
|
|
76
|
+
|
|
77
|
+
return token;
|
|
78
|
+
} catch (error: any) {
|
|
79
|
+
// Silently handle missing aps-environment (push not configured in Xcode)
|
|
80
|
+
const errorMsg = error?.message || String(error);
|
|
81
|
+
if (errorMsg.includes('aps-environment') || errorMsg.includes('messaging/unknown')) {
|
|
82
|
+
console.log('Push notifications not configured in Xcode. App will work without push.');
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
console.warn('Error getting FCM token:', error);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function onForegroundMessage(
|
|
91
|
+
callback: (message: FirebaseMessagingTypes.RemoteMessage) => void
|
|
92
|
+
): () => void {
|
|
93
|
+
const messagingInstance = getMessagingInstance();
|
|
94
|
+
return messagingInstance.onMessage(callback);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function setBackgroundMessageHandler(
|
|
98
|
+
handler: (message: any) => Promise<void>
|
|
99
|
+
): void {
|
|
100
|
+
const messagingInstance = getMessagingInstance();
|
|
101
|
+
messagingInstance.setBackgroundMessageHandler(handler);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function onNotificationOpenedApp(
|
|
105
|
+
callback: (message: any) => void
|
|
106
|
+
): () => void {
|
|
107
|
+
const messagingInstance = getMessagingInstance();
|
|
108
|
+
return messagingInstance.onNotificationOpenedApp(callback);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function getInitialNotification(): Promise<any | null> {
|
|
112
|
+
try {
|
|
113
|
+
const messagingInstance = getMessagingInstance();
|
|
114
|
+
const message = await messagingInstance.getInitialNotification();
|
|
115
|
+
return message;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error('Error getting initial notification:', error);
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function initPushNotifications(): Promise<string | null> {
|
|
123
|
+
|
|
124
|
+
const hasPermission = await requestNotificationPermission();
|
|
125
|
+
|
|
126
|
+
if (!hasPermission) {
|
|
127
|
+
console.error('Push notification permission denied');
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const token = await getFCMToken();
|
|
132
|
+
|
|
133
|
+
return token;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function checkNotificationPermission(): Promise<boolean> {
|
|
137
|
+
try {
|
|
138
|
+
const messagingInstance = getMessagingInstance();
|
|
139
|
+
if (Platform.OS === 'ios') {
|
|
140
|
+
try {
|
|
141
|
+
const apnsToken = await messagingInstance.getAPNSToken();
|
|
142
|
+
return apnsToken !== null;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (Platform.OS === 'android' && Platform.Version >= 33) {
|
|
149
|
+
const granted = await PermissionsAndroid.check(
|
|
150
|
+
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
|
|
151
|
+
);
|
|
152
|
+
return granted;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return true;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.warn('Error checking notification permission:', error);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function disablePushNotifications(): Promise<void> {
|
|
163
|
+
try {
|
|
164
|
+
const messagingInstance = getMessagingInstance();
|
|
165
|
+
await messagingInstance.deleteToken();
|
|
166
|
+
console.log('FCM token deleted successfully');
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.warn('Error deleting FCM token:', error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Client } from '@xmpp/client';
|
|
2
|
+
import { User } from '../types/types';
|
|
3
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
4
|
+
import { subscribeToPushNotifications } from '../networking/api-requests/push.api';
|
|
5
|
+
import { subscribeToRoomMessages } from '../networking/xmpp/subscribeToRoomMessages.xmpp';
|
|
6
|
+
|
|
7
|
+
const SUBSCRIBED_ROOMS_KEY = 'preshent_subscribed_rooms';
|
|
8
|
+
|
|
9
|
+
export class PushSubscriptionService {
|
|
10
|
+
private subscribedRooms: Set<string> = new Set();
|
|
11
|
+
private isPushSubscribed: boolean = false;
|
|
12
|
+
private lastSubscriptionKey: string | null = null;
|
|
13
|
+
private isInitialized: boolean = false;
|
|
14
|
+
|
|
15
|
+
private async loadSubscribedRoomsFromStorage(): Promise<void> {
|
|
16
|
+
if (this.isInitialized) {return;}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const storedRooms = await AsyncStorage.getItem(SUBSCRIBED_ROOMS_KEY);
|
|
20
|
+
if (storedRooms) {
|
|
21
|
+
const roomsArray = JSON.parse(storedRooms) as string[];
|
|
22
|
+
this.subscribedRooms = new Set(roomsArray);
|
|
23
|
+
}
|
|
24
|
+
this.isInitialized = true;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('[PushService] Failed to load subscribed rooms from storage:', error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private async saveSubscribedRoomsToStorage(): Promise<void> {
|
|
31
|
+
try {
|
|
32
|
+
const roomsArray = Array.from(this.subscribedRooms);
|
|
33
|
+
await AsyncStorage.setItem(SUBSCRIBED_ROOMS_KEY, JSON.stringify(roomsArray));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error('[PushService] Failed to save subscribed rooms to storage:', error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async subscribeToPush(
|
|
40
|
+
fcmToken: string,
|
|
41
|
+
user: User,
|
|
42
|
+
projectName: string,
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
const walletAddress = user.defaultWallet?.walletAddress || user.walletAddress;
|
|
45
|
+
const subscriptionKey = `${fcmToken}_${walletAddress}`;
|
|
46
|
+
|
|
47
|
+
if (this.isPushSubscribed && this.lastSubscriptionKey === subscriptionKey) {
|
|
48
|
+
console.log('⚠️ Push already subscribed with this token, skipping...');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const userJid: string = user.xmppUsername || '';
|
|
54
|
+
|
|
55
|
+
if (!userJid) {
|
|
56
|
+
throw new Error('User JID is required for push subscription');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await subscribeToPushNotifications(fcmToken, userJid, projectName);
|
|
60
|
+
this.isPushSubscribed = true;
|
|
61
|
+
this.lastSubscriptionKey = subscriptionKey;
|
|
62
|
+
} catch (error: any) {
|
|
63
|
+
console.error('Failed to subscribe to push after all retries:', error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async subscribeToRoom(
|
|
68
|
+
client: Client,
|
|
69
|
+
roomJID: string,
|
|
70
|
+
userNick?: string
|
|
71
|
+
): Promise<boolean> {
|
|
72
|
+
await this.loadSubscribedRoomsFromStorage();
|
|
73
|
+
|
|
74
|
+
if (this.subscribedRooms.has(roomJID)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const result = await subscribeToRoomMessages(client, roomJID, userNick);
|
|
80
|
+
if (result) {
|
|
81
|
+
this.subscribedRooms.add(roomJID);
|
|
82
|
+
await this.saveSubscribedRoomsToStorage();
|
|
83
|
+
}
|
|
84
|
+
console.log('Successfully Subscribed to room', roomJID);
|
|
85
|
+
return result;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(`[PushService] Failed to subscribe to room ${roomJID}:`, error);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async subscribeToRooms(
|
|
93
|
+
client: Client,
|
|
94
|
+
roomJIDs: string[],
|
|
95
|
+
userNick?: string
|
|
96
|
+
): Promise<void> {
|
|
97
|
+
let successful = 0;
|
|
98
|
+
let failed = 0;
|
|
99
|
+
|
|
100
|
+
console.log('test roomJIDs', roomJIDs);
|
|
101
|
+
|
|
102
|
+
for (const roomJID of roomJIDs) {
|
|
103
|
+
try {
|
|
104
|
+
const result = await this.subscribeToRoom(client, roomJID, userNick);
|
|
105
|
+
console.log('test roomJID-1', result);
|
|
106
|
+
if (result) {
|
|
107
|
+
successful++;
|
|
108
|
+
} else {
|
|
109
|
+
failed++;
|
|
110
|
+
}
|
|
111
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
112
|
+
} catch (error) {
|
|
113
|
+
failed++;
|
|
114
|
+
console.error(`Failed to subscribe to room ${roomJID}:`, error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log(`Subscribed to ${successful} rooms, ${failed} failed`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async reset(): Promise<void> {
|
|
122
|
+
this.subscribedRooms.clear();
|
|
123
|
+
this.isPushSubscribed = false;
|
|
124
|
+
this.lastSubscriptionKey = null;
|
|
125
|
+
this.isInitialized = false;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
await AsyncStorage.removeItem(SUBSCRIBED_ROOMS_KEY);
|
|
129
|
+
console.log('[PushService] Subscribed rooms cleared from storage');
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('[PushService] Failed to clear subscribed rooms from storage:', error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
isRoomSubscribed(roomJID: string): boolean {
|
|
136
|
+
return this.subscribedRooms.has(roomJID);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getSubscribedRooms(): string[] {
|
|
140
|
+
return Array.from(this.subscribedRooms);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const pushSubscriptionService = new PushSubscriptionService();
|
|
145
|
+
|