@ethora/chat-component-rn 26.5.21 → 26.6.10
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/CHANGELOG.md +80 -0
- package/README.md +3 -3
- package/index.js +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +20 -8
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +17 -0
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/LanguageSelectorButton.js +62 -0
- package/lib/commonjs/components/MainComponents/LanguageSelectorButton.js.map +1 -0
- package/lib/commonjs/components/MainComponents/LanguageSelectorModal.js +253 -0
- package/lib/commonjs/components/MainComponents/LanguageSelectorModal.js.map +1 -0
- package/lib/commonjs/components/MainComponents/RoomList.js +23 -9
- package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/Message.js +72 -11
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageTranslate.js +160 -0
- package/lib/commonjs/components/MessageBubble/MessageTranslate.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/TranslatedMessageBody.js +59 -0
- package/lib/commonjs/components/MessageBubble/TranslatedMessageBody.js.map +1 -0
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +3 -3
- package/lib/commonjs/components/UsersList/UsersList.js +3 -1
- package/lib/commonjs/components/UsersList/UsersList.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/CallButtons.js +159 -0
- package/lib/commonjs/components/VideoCalls/CallButtons.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/CallIcons.js +328 -0
- package/lib/commonjs/components/VideoCalls/CallIcons.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +550 -0
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js +636 -0
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js +91 -0
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/useCallKeep.js +215 -0
- package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -0
- package/lib/commonjs/components/styled/AudioMessage.js +91 -33
- package/lib/commonjs/components/styled/AudioMessage.js.map +1 -1
- package/lib/commonjs/components/styled/RoomListComponents/index.js +13 -1
- package/lib/commonjs/components/styled/RoomListComponents/index.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +83 -49
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/components/styled/StyledComponents.js +9 -4
- package/lib/commonjs/components/styled/StyledComponents.js.map +1 -1
- package/lib/commonjs/components/styled/StyledInputComponents/Composing.js +2 -0
- package/lib/commonjs/components/styled/StyledInputComponents/Composing.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +10 -3
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/callLogMessage.js +162 -0
- package/lib/commonjs/helpers/callLogMessage.js.map +1 -0
- package/lib/commonjs/helpers/callPush.js +61 -0
- package/lib/commonjs/helpers/callPush.js.map +1 -0
- package/lib/commonjs/helpers/constants/LANGUAGE_OPTIONS.js +24 -12
- package/lib/commonjs/helpers/constants/LANGUAGE_OPTIONS.js.map +1 -1
- package/lib/commonjs/helpers/createUserNameFromSetUser.js +51 -2
- package/lib/commonjs/helpers/createUserNameFromSetUser.js.map +1 -1
- package/lib/commonjs/helpers/getDataFromXml.js +19 -1
- package/lib/commonjs/helpers/getDataFromXml.js.map +1 -1
- package/lib/commonjs/helpers/incomingCallFromPush.js +74 -0
- package/lib/commonjs/helpers/incomingCallFromPush.js.map +1 -0
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js +13 -1
- package/lib/commonjs/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/commonjs/helpers/isUnresolvedSenderId.js +32 -0
- package/lib/commonjs/helpers/isUnresolvedSenderId.js.map +1 -0
- package/lib/commonjs/hooks/useChatSettingState.js +2 -0
- package/lib/commonjs/hooks/useChatSettingState.js.map +1 -1
- package/lib/commonjs/hooks/useMessageTranslation.js +51 -0
- package/lib/commonjs/hooks/useMessageTranslation.js.map +1 -0
- package/lib/commonjs/hooks/usePushNotifications.js +13 -3
- package/lib/commonjs/hooks/usePushNotifications.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +12 -2
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/i18n/strings.js +1308 -0
- package/lib/commonjs/i18n/strings.js.map +1 -0
- package/lib/commonjs/i18n/useT.js +60 -0
- package/lib/commonjs/i18n/useT.js.map +1 -0
- package/lib/commonjs/main.js +44 -0
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/api-requests/push.api.js +34 -5
- package/lib/commonjs/networking/api-requests/push.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/rooms.api.js +108 -0
- package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/commonjs/networking/callTokenStanza.js +531 -0
- package/lib/commonjs/networking/callTokenStanza.js.map +1 -0
- package/lib/commonjs/networking/outboundQueue.js.map +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js +75 -8
- package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +47 -11
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js +2 -1
- package/lib/commonjs/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +42 -5
- package/lib/commonjs/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js +78 -16
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/callSlice.js +144 -0
- package/lib/commonjs/roomStore/callSlice.js.map +1 -0
- package/lib/commonjs/roomStore/chatSettingsSlice.js +8 -2
- package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/commonjs/roomStore/index.js +7 -1
- package/lib/commonjs/roomStore/index.js.map +1 -1
- package/lib/commonjs/roomStore/persistence.js +69 -5
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +136 -6
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/commonjs/utils/translateModePolicy.js +33 -0
- package/lib/commonjs/utils/translateModePolicy.js.map +1 -0
- package/lib/module/components/MainComponents/ChatHeader.js +20 -8
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +17 -0
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/LanguageSelectorButton.js +57 -0
- package/lib/module/components/MainComponents/LanguageSelectorButton.js.map +1 -0
- package/lib/module/components/MainComponents/LanguageSelectorModal.js +246 -0
- package/lib/module/components/MainComponents/LanguageSelectorModal.js.map +1 -0
- package/lib/module/components/MainComponents/RoomList.js +23 -9
- package/lib/module/components/MainComponents/RoomList.js.map +1 -1
- package/lib/module/components/MessageBubble/Message.js +73 -12
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageTranslate.js +155 -0
- package/lib/module/components/MessageBubble/MessageTranslate.js.map +1 -0
- package/lib/module/components/MessageBubble/TranslatedMessageBody.js +53 -0
- package/lib/module/components/MessageBubble/TranslatedMessageBody.js.map +1 -0
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +3 -3
- package/lib/module/components/UsersList/UsersList.js +3 -1
- package/lib/module/components/UsersList/UsersList.js.map +1 -1
- package/lib/module/components/VideoCalls/CallButtons.js +154 -0
- package/lib/module/components/VideoCalls/CallButtons.js.map +1 -0
- package/lib/module/components/VideoCalls/CallIcons.js +309 -0
- package/lib/module/components/VideoCalls/CallIcons.js.map +1 -0
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +544 -0
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -0
- package/lib/module/components/VideoCalls/VideoCallSession.js +630 -0
- package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -0
- package/lib/module/components/VideoCalls/livekitRuntime.js +83 -0
- package/lib/module/components/VideoCalls/livekitRuntime.js.map +1 -0
- package/lib/module/components/VideoCalls/useCallKeep.js +210 -0
- package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -0
- package/lib/module/components/styled/AudioMessage.js +91 -33
- package/lib/module/components/styled/AudioMessage.js.map +1 -1
- package/lib/module/components/styled/RoomListComponents/index.js +13 -1
- package/lib/module/components/styled/RoomListComponents/index.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +83 -49
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/components/styled/StyledComponents.js +9 -4
- package/lib/module/components/styled/StyledComponents.js.map +1 -1
- package/lib/module/components/styled/StyledInputComponents/Composing.js +2 -0
- package/lib/module/components/styled/StyledInputComponents/Composing.js.map +1 -1
- package/lib/module/context/xmppProvider.js +11 -4
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/callLogMessage.js +152 -0
- package/lib/module/helpers/callLogMessage.js.map +1 -0
- package/lib/module/helpers/callPush.js +55 -0
- package/lib/module/helpers/callPush.js.map +1 -0
- package/lib/module/helpers/constants/LANGUAGE_OPTIONS.js +24 -12
- package/lib/module/helpers/constants/LANGUAGE_OPTIONS.js.map +1 -1
- package/lib/module/helpers/createUserNameFromSetUser.js +49 -1
- package/lib/module/helpers/createUserNameFromSetUser.js.map +1 -1
- package/lib/module/helpers/getDataFromXml.js +20 -1
- package/lib/module/helpers/getDataFromXml.js.map +1 -1
- package/lib/module/helpers/incomingCallFromPush.js +70 -0
- package/lib/module/helpers/incomingCallFromPush.js.map +1 -0
- package/lib/module/helpers/insertMessageWithDelimiter.js +13 -1
- package/lib/module/helpers/insertMessageWithDelimiter.js.map +1 -1
- package/lib/module/helpers/isUnresolvedSenderId.js +27 -0
- package/lib/module/helpers/isUnresolvedSenderId.js.map +1 -0
- package/lib/module/hooks/useChatSettingState.js +2 -0
- package/lib/module/hooks/useChatSettingState.js.map +1 -1
- package/lib/module/hooks/useMessageTranslation.js +46 -0
- package/lib/module/hooks/useMessageTranslation.js.map +1 -0
- package/lib/module/hooks/usePushNotifications.js +13 -3
- package/lib/module/hooks/usePushNotifications.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +12 -2
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/i18n/strings.js +1300 -0
- package/lib/module/i18n/strings.js.map +1 -0
- package/lib/module/i18n/useT.js +55 -0
- package/lib/module/i18n/useT.js.map +1 -0
- package/lib/module/main.js +7 -1
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/api-requests/push.api.js +35 -5
- package/lib/module/networking/api-requests/push.api.js.map +1 -1
- package/lib/module/networking/api-requests/rooms.api.js +109 -1
- package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/module/networking/callTokenStanza.js +526 -0
- package/lib/module/networking/callTokenStanza.js.map +1 -0
- package/lib/module/networking/outboundQueue.js.map +1 -1
- package/lib/module/networking/stanzaHandlers.js +76 -9
- package/lib/module/networking/stanzaHandlers.js.map +1 -1
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js +48 -11
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/sendTextMessage.xmpp.js +1 -1
- package/lib/module/networking/xmpp/sendTextMessage.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js +43 -5
- package/lib/module/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.js.map +1 -1
- package/lib/module/networking/xmppClient.js +78 -16
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/callSlice.js +130 -0
- package/lib/module/roomStore/callSlice.js.map +1 -0
- package/lib/module/roomStore/chatSettingsSlice.js +6 -1
- package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/module/roomStore/index.js +6 -1
- package/lib/module/roomStore/index.js.map +1 -1
- package/lib/module/roomStore/persistence.js +69 -5
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +134 -5
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/utils/translateModePolicy.js +27 -0
- package/lib/module/utils/translateModePolicy.js.map +1 -0
- package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/LanguageSelectorButton.d.ts +12 -0
- package/lib/typescript/components/MainComponents/LanguageSelectorButton.d.ts.map +1 -0
- package/lib/typescript/components/MainComponents/LanguageSelectorModal.d.ts +31 -0
- package/lib/typescript/components/MainComponents/LanguageSelectorModal.d.ts.map +1 -0
- package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageTranslate.d.ts +44 -0
- package/lib/typescript/components/MessageBubble/MessageTranslate.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble/TranslatedMessageBody.d.ts +24 -0
- package/lib/typescript/components/MessageBubble/TranslatedMessageBody.d.ts.map +1 -0
- package/lib/typescript/components/UsersList/UsersList.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/CallButtons.d.ts +16 -0
- package/lib/typescript/components/VideoCalls/CallButtons.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts +30 -0
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts +10 -0
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts +38 -0
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts +52 -0
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts +32 -0
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -0
- package/lib/typescript/components/styled/AudioMessage.d.ts.map +1 -1
- package/lib/typescript/components/styled/RoomListComponents/index.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/components/styled/StyledComponents.d.ts.map +1 -1
- package/lib/typescript/components/styled/StyledInputComponents/Composing.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/callLogMessage.d.ts +34 -0
- package/lib/typescript/helpers/callLogMessage.d.ts.map +1 -0
- package/lib/typescript/helpers/callPush.d.ts +23 -0
- package/lib/typescript/helpers/callPush.d.ts.map +1 -0
- package/lib/typescript/helpers/constants/LANGUAGE_OPTIONS.d.ts +14 -0
- package/lib/typescript/helpers/constants/LANGUAGE_OPTIONS.d.ts.map +1 -1
- package/lib/typescript/helpers/createUserNameFromSetUser.d.ts +33 -1
- package/lib/typescript/helpers/createUserNameFromSetUser.d.ts.map +1 -1
- package/lib/typescript/helpers/getDataFromXml.d.ts.map +1 -1
- package/lib/typescript/helpers/incomingCallFromPush.d.ts +14 -0
- package/lib/typescript/helpers/incomingCallFromPush.d.ts.map +1 -0
- package/lib/typescript/helpers/insertMessageWithDelimiter.d.ts.map +1 -1
- package/lib/typescript/helpers/isUnresolvedSenderId.d.ts +2 -0
- package/lib/typescript/helpers/isUnresolvedSenderId.d.ts.map +1 -0
- package/lib/typescript/hooks/useChatSettingState.d.ts +2 -1
- package/lib/typescript/hooks/useChatSettingState.d.ts.map +1 -1
- package/lib/typescript/hooks/useMessageTranslation.d.ts +25 -0
- package/lib/typescript/hooks/useMessageTranslation.d.ts.map +1 -0
- package/lib/typescript/hooks/usePushNotifications.d.ts.map +1 -1
- package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/i18n/strings.d.ts +16 -0
- package/lib/typescript/i18n/strings.d.ts.map +1 -0
- package/lib/typescript/i18n/useT.d.ts +34 -0
- package/lib/typescript/i18n/useT.d.ts.map +1 -0
- package/lib/typescript/main.d.ts +6 -1
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/push.api.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/rooms.api.d.ts +3 -0
- package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
- package/lib/typescript/networking/callTokenStanza.d.ts +14 -0
- package/lib/typescript/networking/callTokenStanza.d.ts.map +1 -0
- package/lib/typescript/networking/outboundQueue.d.ts +1 -0
- package/lib/typescript/networking/outboundQueue.d.ts.map +1 -1
- package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/handleStanzas.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts +10 -0
- package/lib/typescript/networking/xmpp/sendTextMessage.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts +16 -1
- package/lib/typescript/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts +1 -1
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/callSlice.d.ts +39 -0
- package/lib/typescript/roomStore/callSlice.d.ts.map +1 -0
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts +10 -1
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
- package/lib/typescript/roomStore/index.d.ts +4 -0
- package/lib/typescript/roomStore/index.d.ts.map +1 -1
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts +6 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/types/models/language.model.d.ts +1 -1
- package/lib/typescript/types/models/language.model.d.ts.map +1 -1
- package/lib/typescript/types/models/message.model.d.ts +7 -0
- package/lib/typescript/types/models/message.model.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +135 -1
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/utils/translateModePolicy.d.ts +20 -0
- package/lib/typescript/utils/translateModePolicy.d.ts.map +1 -0
- package/package.json +20 -4
- package/src/components/MainComponents/ChatHeader.tsx +30 -9
- package/src/components/MainComponents/ChatWrapper.tsx +15 -0
- package/src/components/MainComponents/LanguageSelectorButton.tsx +53 -0
- package/src/components/MainComponents/LanguageSelectorModal.tsx +276 -0
- package/src/components/MainComponents/RoomList.tsx +27 -9
- package/src/components/MessageBubble/Message.tsx +109 -17
- package/src/components/MessageBubble/MessageTranslate.tsx +182 -0
- package/src/components/MessageBubble/TranslatedMessageBody.tsx +59 -0
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +3 -3
- package/src/components/UsersList/UsersList.tsx +3 -1
- package/src/components/VideoCalls/CallButtons.tsx +171 -0
- package/src/components/VideoCalls/CallIcons.tsx +246 -0
- package/src/components/VideoCalls/VideoCallOverlay.tsx +627 -0
- package/src/components/VideoCalls/VideoCallSession.tsx +675 -0
- package/src/components/VideoCalls/livekitRuntime.ts +100 -0
- package/src/components/VideoCalls/useCallKeep.ts +252 -0
- package/src/components/styled/AudioMessage.tsx +97 -37
- package/src/components/styled/RoomListComponents/index.tsx +15 -3
- package/src/components/styled/SendInput.tsx +94 -51
- package/src/components/styled/StyledComponents.ts +9 -6
- package/src/components/styled/StyledInputComponents/Composing.tsx +7 -1
- package/src/context/xmppProvider.tsx +14 -1
- package/src/helpers/callLogMessage.ts +174 -0
- package/src/helpers/callPush.ts +90 -0
- package/src/helpers/constants/LANGUAGE_OPTIONS.ts +20 -5
- package/src/helpers/createUserNameFromSetUser.ts +68 -2
- package/src/helpers/getDataFromXml.ts +40 -4
- package/src/helpers/incomingCallFromPush.ts +83 -0
- package/src/helpers/insertMessageWithDelimiter.ts +27 -9
- package/src/helpers/isUnresolvedSenderId.ts +25 -0
- package/src/hooks/useChatSettingState.tsx +4 -0
- package/src/hooks/useMessageTranslation.ts +70 -0
- package/src/hooks/usePushNotifications.ts +15 -3
- package/src/hooks/useSendMessage.tsx +15 -2
- package/src/i18n/strings.ts +1322 -0
- package/src/i18n/useT.ts +56 -0
- package/src/main.ts +18 -1
- package/src/networking/api-requests/push.api.ts +39 -8
- package/src/networking/api-requests/rooms.api.ts +109 -1
- package/src/networking/callTokenStanza.ts +636 -0
- package/src/networking/outboundQueue.ts +1 -0
- package/src/networking/stanzaHandlers.ts +74 -15
- package/src/networking/xmpp/handleStanzas.xmpp.ts +52 -11
- package/src/networking/xmpp/sendTextMessage.xmpp.ts +1 -1
- package/src/networking/xmpp/sendTextMessageWithTranslateTag.xmpp.ts +41 -5
- package/src/networking/xmppClient.ts +97 -28
- package/src/roomStore/callSlice.ts +190 -0
- package/src/roomStore/chatSettingsSlice.ts +16 -0
- package/src/roomStore/index.ts +5 -0
- package/src/roomStore/persistence.ts +103 -3
- package/src/roomStore/roomsSlice.ts +149 -4
- package/src/types/models/language.model.ts +17 -1
- package/src/types/models/message.model.ts +9 -0
- package/src/types/types.ts +142 -2
- package/src/utils/translateModePolicy.ts +31 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
User,
|
|
11
11
|
} from '../types/types';
|
|
12
12
|
import { Iso639_1Codes } from '../types/models/language.model';
|
|
13
|
+
import type { TranslateMode } from '../utils/translateModePolicy';
|
|
13
14
|
import { localStorageConstants } from '../helpers/constants/LOCAL_STORAGE';
|
|
14
15
|
import { asyncLocalStorage } from '../hooks/useLocalStorage';
|
|
15
16
|
|
|
@@ -22,6 +23,13 @@ export interface ChatState {
|
|
|
22
23
|
activeFile?: ModalFile;
|
|
23
24
|
client?: any;
|
|
24
25
|
langSource?: Iso639_1Codes;
|
|
26
|
+
/**
|
|
27
|
+
* The reader's own auto/manual pick from the language modal's switcher.
|
|
28
|
+
* Undefined until they touch it, at which point it wins over the host's
|
|
29
|
+
* `config.translates.mode` default (unless the host pinned `forceType`).
|
|
30
|
+
* See utils/translateModePolicy.
|
|
31
|
+
*/
|
|
32
|
+
translateMode?: TranslateMode;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
export const unpackAndTransform = (input?: User): User => {
|
|
@@ -152,6 +160,7 @@ const reducers = {
|
|
|
152
160
|
state.config = undefined;
|
|
153
161
|
state.client = undefined;
|
|
154
162
|
state.langSource = undefined;
|
|
163
|
+
state.translateMode = undefined;
|
|
155
164
|
asyncLocalStorage(localStorageConstants.ETHORA_USER).remove();
|
|
156
165
|
},
|
|
157
166
|
setLangSource: (
|
|
@@ -160,6 +169,12 @@ const reducers = {
|
|
|
160
169
|
) => {
|
|
161
170
|
state.langSource = action.payload;
|
|
162
171
|
},
|
|
172
|
+
setTranslateMode: (
|
|
173
|
+
state: WritableDraft<ChatState>,
|
|
174
|
+
action: PayloadAction<TranslateMode | undefined>
|
|
175
|
+
) => {
|
|
176
|
+
state.translateMode = action.payload;
|
|
177
|
+
},
|
|
163
178
|
};
|
|
164
179
|
|
|
165
180
|
export const chatSlice: Slice<ChatState, typeof reducers, 'chat'> = createSlice({
|
|
@@ -180,6 +195,7 @@ export const {
|
|
|
180
195
|
setActiveFile,
|
|
181
196
|
setStoreClient,
|
|
182
197
|
setLangSource,
|
|
198
|
+
setTranslateMode,
|
|
183
199
|
} = chatSlice.actions;
|
|
184
200
|
|
|
185
201
|
export default chatSlice.reducer;
|
package/src/roomStore/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { configureStore, combineReducers } from '@reduxjs/toolkit';
|
|
|
2
2
|
import chatSettingsReducer, { setUser } from './chatSettingsSlice';
|
|
3
3
|
import roomsSlice, { addRoom } from './roomsSlice';
|
|
4
4
|
import { roomHeapSlice } from './roomHeapSlice';
|
|
5
|
+
import callReducer from './callSlice';
|
|
5
6
|
import { IRoom } from '../types/types';
|
|
6
7
|
import { unreadMiddleware } from './Middleware/unreadMidlleware';
|
|
7
8
|
import { logoutMiddleware } from './Middleware/logoutMiddleware';
|
|
@@ -13,6 +14,10 @@ const rootReducer = combineReducers({
|
|
|
13
14
|
chatSettingStore: chatSettingsReducer,
|
|
14
15
|
rooms: roomsSlice,
|
|
15
16
|
roomHeapSlice: roomHeapSlice.reducer,
|
|
17
|
+
// Live call state. Deliberately NOT persisted: a call that was ringing
|
|
18
|
+
// when the app was killed is over by the time it reopens, and restoring
|
|
19
|
+
// it would put the user straight into a dead LiveKit room.
|
|
20
|
+
call: callReducer,
|
|
16
21
|
});
|
|
17
22
|
|
|
18
23
|
export type RootState = ReturnType<typeof rootReducer>;
|
|
@@ -49,10 +49,99 @@ const sanitizeUser = (user?: User): User | null => {
|
|
|
49
49
|
} as User;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
// What a persisted message is FOR: instantly painting a recent transcript
|
|
53
|
+
// on reload before MAM catches up. That needs the fields the bubbles and
|
|
54
|
+
// room-list previews actually read, nothing else. Everything outside this
|
|
55
|
+
// list is either re-derived on render or re-fetched from the server.
|
|
56
|
+
const PERSISTED_MESSAGE_FIELDS: (keyof IMessage)[] = [
|
|
57
|
+
'id',
|
|
58
|
+
'xmppId',
|
|
59
|
+
'xmppFrom',
|
|
60
|
+
'body',
|
|
61
|
+
'date',
|
|
62
|
+
'timestamp',
|
|
63
|
+
'roomJid',
|
|
64
|
+
'isSystemMessage',
|
|
65
|
+
'isMediafile',
|
|
66
|
+
'isDeleted',
|
|
67
|
+
'isEdited',
|
|
68
|
+
'isReply',
|
|
69
|
+
'showInChannel',
|
|
70
|
+
'mainMessage',
|
|
71
|
+
'mimetype',
|
|
72
|
+
'location',
|
|
73
|
+
'locationPreview',
|
|
74
|
+
'fileName',
|
|
75
|
+
'originalName',
|
|
76
|
+
'size',
|
|
77
|
+
'langSource',
|
|
78
|
+
'callLog',
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
// The sender identity that rides along on every message over the wire
|
|
82
|
+
// (senderFirstName / senderLastName / photo, which createMessageFromXml
|
|
83
|
+
// folds into message.user) is NOT what the UI reads back, and is not ours
|
|
84
|
+
// to cache: `usersSet` is the canonical store for names and avatars, and
|
|
85
|
+
// the renderers resolve through it, re-deriving the name every time
|
|
86
|
+
// usersSet updates. That is what keeps a renamed user from staying stale.
|
|
87
|
+
//
|
|
88
|
+
// Persisting a copy per message duplicated the same handful of identities
|
|
89
|
+
// across every message of every room, and optimistic sends spread the
|
|
90
|
+
// ENTIRE logged-in user into message.user (see useSendMessage), auth
|
|
91
|
+
// material included.
|
|
92
|
+
//
|
|
93
|
+
// Keep `id` (the key usersSet is looked up by) and `name`, nothing else.
|
|
94
|
+
// `name` earns its ~15 chars: broadcast/system senders ("Ethora") never
|
|
95
|
+
// enter usersSet at all, so for those the message is the only place the
|
|
96
|
+
// name exists.
|
|
97
|
+
const PERSISTED_MESSAGE_USER_FIELDS = ['id', 'name'] as const;
|
|
98
|
+
|
|
99
|
+
// Room fields that are pure server state, re-fetched on every load, and so
|
|
100
|
+
// must never sit in the message cache, let alone compete with messages for
|
|
101
|
+
// AsyncStorage space.
|
|
102
|
+
//
|
|
103
|
+
// `members` is the one that matters. A 3.5k-member room serializes to
|
|
104
|
+
// roughly 840k chars, several hundred times the rest of the room object
|
|
105
|
+
// put together. A handful of such rooms is megabytes of roster written on
|
|
106
|
+
// every debounce tick, and on Android AsyncStorage that means blown
|
|
107
|
+
// cursor-window limits and multi-second writes, for data createRoomFromApi
|
|
108
|
+
// repopulates from /chats/my on the very next load. `usersCnt`, which the
|
|
109
|
+
// header actually reads, is its own scalar field and is preserved below.
|
|
110
|
+
const REFETCHED_ROOM_FIELDS = ['members'] as const;
|
|
111
|
+
|
|
112
|
+
const pickDefined = <T extends object>(
|
|
113
|
+
source: T,
|
|
114
|
+
keys: readonly (keyof T)[]
|
|
115
|
+
): Partial<T> => {
|
|
116
|
+
const out: Partial<T> = {};
|
|
117
|
+
for (const key of keys) {
|
|
118
|
+
const value = source?.[key];
|
|
119
|
+
if (value !== undefined) {
|
|
120
|
+
out[key] = value;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const compactMessageForPersist = (message: IMessage): IMessage => {
|
|
127
|
+
const compact = pickDefined(message, PERSISTED_MESSAGE_FIELDS) as IMessage;
|
|
128
|
+
const user = message?.user as Record<string, any> | undefined;
|
|
129
|
+
if (user) {
|
|
130
|
+
compact.user = pickDefined(
|
|
131
|
+
user,
|
|
132
|
+
PERSISTED_MESSAGE_USER_FIELDS as unknown as readonly string[]
|
|
133
|
+
) as IMessage['user'];
|
|
134
|
+
}
|
|
135
|
+
return compact;
|
|
136
|
+
};
|
|
137
|
+
|
|
52
138
|
const sanitizeMessages = (messages: IMessage[]): IMessage[] => {
|
|
53
139
|
if (!Array.isArray(messages)) {return [];}
|
|
54
|
-
|
|
55
|
-
|
|
140
|
+
const capped =
|
|
141
|
+
messages.length > MESSAGE_LIMIT
|
|
142
|
+
? messages.slice(-MESSAGE_LIMIT)
|
|
143
|
+
: messages;
|
|
144
|
+
return capped.map(compactMessageForPersist);
|
|
56
145
|
};
|
|
57
146
|
|
|
58
147
|
const sanitizeRooms = (
|
|
@@ -63,8 +152,19 @@ const sanitizeRooms = (
|
|
|
63
152
|
for (const [jid, room] of Object.entries(rooms)) {
|
|
64
153
|
if (!jid || typeof jid !== 'string' || !jid.includes('@')) {continue;}
|
|
65
154
|
if (!room || typeof room !== 'object' || Array.isArray(room)) {continue;}
|
|
155
|
+
|
|
156
|
+
const compactRoom = { ...room } as Record<string, any>;
|
|
157
|
+
// Preserve the count the header reads before dropping the roster it
|
|
158
|
+
// would otherwise be derived from.
|
|
159
|
+
if (compactRoom.usersCnt === undefined && Array.isArray(room.members)) {
|
|
160
|
+
compactRoom.usersCnt = room.members.length;
|
|
161
|
+
}
|
|
162
|
+
for (const field of REFETCHED_ROOM_FIELDS) {
|
|
163
|
+
delete compactRoom[field];
|
|
164
|
+
}
|
|
165
|
+
|
|
66
166
|
out[jid] = {
|
|
67
|
-
...
|
|
167
|
+
...(compactRoom as IRoom),
|
|
68
168
|
messages: sanitizeMessages(room?.messages || []),
|
|
69
169
|
composing: false,
|
|
70
170
|
composingList: [],
|
|
@@ -15,6 +15,97 @@ import type XmppClient from '../networking/xmppClient';
|
|
|
15
15
|
// just fetched. After cap eviction we drop from the head (oldest).
|
|
16
16
|
const RUNTIME_MESSAGE_LIMIT = 100;
|
|
17
17
|
|
|
18
|
+
// Body strings the server uses for call signaling broadcasts (call-token,
|
|
19
|
+
// call-state ringing/ended, etc). These should never reach the chat
|
|
20
|
+
// transcript or the room-list "last message" preview, they're control
|
|
21
|
+
// frames, not user-visible content. The XMPP handler (onCallTokenMessage)
|
|
22
|
+
// already swallows them in the live stream, but they can still arrive via
|
|
23
|
+
// MAM history, mucsub catchup, or be present in persisted state from
|
|
24
|
+
// before that filter existed. Drop them at the reducer boundary so the
|
|
25
|
+
// transcript stays clean regardless of source.
|
|
26
|
+
const CALL_SIGNAL_BODIES = new Set([
|
|
27
|
+
'call-token',
|
|
28
|
+
'call-state',
|
|
29
|
+
'call-ringing',
|
|
30
|
+
'call-ended',
|
|
31
|
+
'call-declined',
|
|
32
|
+
'call-cancelled',
|
|
33
|
+
'call-canceled',
|
|
34
|
+
'call-timeout',
|
|
35
|
+
'call-rejected',
|
|
36
|
+
'call-invite',
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const isCallSignalMessage = (message: IMessage | undefined | null): boolean => {
|
|
40
|
+
if (!message) {return false;}
|
|
41
|
+
const body = String(message.body || '').trim().toLowerCase();
|
|
42
|
+
return CALL_SIGNAL_BODIES.has(body);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const stripCallSignals = (messages: IMessage[] | undefined): IMessage[] =>
|
|
46
|
+
Array.isArray(messages)
|
|
47
|
+
? messages.filter((message) => !isCallSignalMessage(message))
|
|
48
|
+
: [];
|
|
49
|
+
|
|
50
|
+
// True for the client-side call-log fallback written at hangup: it exists
|
|
51
|
+
// only in this client's store (id "calllog-<callId>"), never on the server.
|
|
52
|
+
const isLocalCallLogEntry = (message: IMessage | undefined | null): boolean =>
|
|
53
|
+
String(message?.id || '').startsWith('calllog-');
|
|
54
|
+
|
|
55
|
+
// Merge two log entries for the SAME callId into one. Identity (id/date/
|
|
56
|
+
// xmppId) comes from the server copy when one side is the local fallback,
|
|
57
|
+
// the server archive id is what MAM pages and the catch-up anchor will
|
|
58
|
+
// match against later. Display (body/callLog) comes from whichever copy
|
|
59
|
+
// saw the larger duration, so partial per-participant call-states don't
|
|
60
|
+
// shrink it.
|
|
61
|
+
const mergeCallLogEntries = (a: IMessage, b: IMessage): IMessage => {
|
|
62
|
+
const aLocal = isLocalCallLogEntry(a);
|
|
63
|
+
const bLocal = isLocalCallLogEntry(b);
|
|
64
|
+
const identity = aLocal === bLocal ? a : aLocal ? b : a;
|
|
65
|
+
const display =
|
|
66
|
+
(a.callLog?.durationMs || 0) >= (b.callLog?.durationMs || 0) ? a : b;
|
|
67
|
+
if (identity === display) {return identity;}
|
|
68
|
+
return {
|
|
69
|
+
...display,
|
|
70
|
+
id: identity.id,
|
|
71
|
+
xmppId: identity.xmppId ?? display.xmppId,
|
|
72
|
+
date: identity.date ?? display.date,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Collapse call-log duplicates for the same callId inside a merged history
|
|
77
|
+
// list (the live path dedups in addRoomMessage, but a MAM page merged over
|
|
78
|
+
// a persisted local fallback entry would otherwise show the call twice).
|
|
79
|
+
const collapseCallLogDuplicates = (messages: IMessage[]): IMessage[] => {
|
|
80
|
+
const byCallId = new Map<string, IMessage>();
|
|
81
|
+
let hasDuplicates = false;
|
|
82
|
+
for (const message of messages) {
|
|
83
|
+
const callId = message?.callLog?.callId;
|
|
84
|
+
if (!callId) {continue;}
|
|
85
|
+
const existing = byCallId.get(callId);
|
|
86
|
+
if (existing) {
|
|
87
|
+
hasDuplicates = true;
|
|
88
|
+
byCallId.set(callId, mergeCallLogEntries(existing, message));
|
|
89
|
+
} else {
|
|
90
|
+
byCallId.set(callId, message);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!hasDuplicates) {return messages;}
|
|
94
|
+
const emitted = new Set<string>();
|
|
95
|
+
return messages
|
|
96
|
+
.filter((message) => {
|
|
97
|
+
const callId = message?.callLog?.callId;
|
|
98
|
+
if (!callId) {return true;}
|
|
99
|
+
if (emitted.has(callId)) {return false;}
|
|
100
|
+
emitted.add(callId);
|
|
101
|
+
return true;
|
|
102
|
+
})
|
|
103
|
+
.map((message) => {
|
|
104
|
+
const callId = message?.callLog?.callId;
|
|
105
|
+
return callId ? byCallId.get(callId) || message : message;
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
|
|
18
109
|
const enforceMessageCap = (messages: IMessage[]): void => {
|
|
19
110
|
// Trim oldest until we're at/under the limit. Mutates in place
|
|
20
111
|
// (immer-compatible inside reducers).
|
|
@@ -45,8 +136,11 @@ function mergeHistoryIntoCache(
|
|
|
45
136
|
existing: IMessage[] | undefined,
|
|
46
137
|
fetched: IMessage[] | undefined
|
|
47
138
|
): IMessage[] {
|
|
48
|
-
|
|
49
|
-
|
|
139
|
+
// Strip call control frames on BOTH sides before merging: they can be
|
|
140
|
+
// in the cache (persisted from before this filter existed) and in the
|
|
141
|
+
// fetched MAM page (the live-stream swallow doesn't cover history).
|
|
142
|
+
const ex = stripCallSignals(existing);
|
|
143
|
+
const fe = stripCallSignals(fetched);
|
|
50
144
|
|
|
51
145
|
const pending = ex.filter((m) => m?.pending);
|
|
52
146
|
const realExisting = ex.filter(
|
|
@@ -69,7 +163,10 @@ function mergeHistoryIntoCache(
|
|
|
69
163
|
|
|
70
164
|
// Cold room, or a gap (no shared id) → can't merge → use the fresh page.
|
|
71
165
|
if (realExisting.length === 0 || !overlaps) {
|
|
72
|
-
return [
|
|
166
|
+
return [
|
|
167
|
+
...capTail(collapseCallLogDuplicates(realFetched.slice().sort(byMs))),
|
|
168
|
+
...pending,
|
|
169
|
+
];
|
|
73
170
|
}
|
|
74
171
|
|
|
75
172
|
// Overlap → union + dedupe by id. Fetched wins on collision: it carries
|
|
@@ -87,7 +184,9 @@ function mergeHistoryIntoCache(
|
|
|
87
184
|
prev?.isEdited && !m.isEdited ? { ...m, isEdited: true } : m
|
|
88
185
|
);
|
|
89
186
|
}
|
|
90
|
-
const merged =
|
|
187
|
+
const merged = collapseCallLogDuplicates(
|
|
188
|
+
Array.from(byId.values()).sort(byMs)
|
|
189
|
+
);
|
|
91
190
|
return [...capTail(merged), ...pending];
|
|
92
191
|
}
|
|
93
192
|
|
|
@@ -335,6 +434,14 @@ const reducers = {
|
|
|
335
434
|
) {
|
|
336
435
|
const { roomJID, message, start } = action.payload;
|
|
337
436
|
|
|
437
|
+
// Call signaling broadcasts ("call-token", "call-state", etc.)
|
|
438
|
+
// sometimes slip past the live XMPP filter (MAM history, mucsub
|
|
439
|
+
// catch-up), drop them here so they never land in the transcript
|
|
440
|
+
// or the room-list "last message" preview.
|
|
441
|
+
if (isCallSignalMessage(message)) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
338
445
|
// Guard against the "stanza arrived before /chats/my completed"
|
|
339
446
|
// race — without this, the optional-chain `?.messages` falls
|
|
340
447
|
// back to undefined, and the assignment below tries to set
|
|
@@ -350,6 +457,33 @@ const reducers = {
|
|
|
350
457
|
}
|
|
351
458
|
|
|
352
459
|
const roomMessages = state.rooms[roomJID].messages;
|
|
460
|
+
|
|
461
|
+
// Collapse multiple call-state events for the same call into a single
|
|
462
|
+
// log entry. Sources: the client-side fallback written at hangup (id
|
|
463
|
+
// "calllog-<callId>", exists only locally) and the server broadcast(s),
|
|
464
|
+
// which can fire once per participant leaving (earlier ones carry a
|
|
465
|
+
// partial durationMs). Rules:
|
|
466
|
+
// - the SERVER copy is canonical for identity (id/date/xmppId): its
|
|
467
|
+
// archive id is what MAM and the catch-up anchor return later, so
|
|
468
|
+
// keeping a local "calllog-" id around breaks anchor matching and
|
|
469
|
+
// duplicates the entry on the next history merge;
|
|
470
|
+
// - the LARGEST duration wins for display, so a 2-minute call doesn't
|
|
471
|
+
// render as "2 sec".
|
|
472
|
+
const incomingCallLog = message.callLog;
|
|
473
|
+
if (incomingCallLog?.callId) {
|
|
474
|
+
const existingCallIdx = roomMessages.findIndex(
|
|
475
|
+
(msg) => msg.callLog?.callId === incomingCallLog.callId
|
|
476
|
+
);
|
|
477
|
+
if (existingCallIdx !== -1) {
|
|
478
|
+
const existing = roomMessages[existingCallIdx];
|
|
479
|
+
const merged = mergeCallLogEntries(existing as IMessage, message);
|
|
480
|
+
if (merged !== (existing as IMessage)) {
|
|
481
|
+
roomMessages[existingCallIdx] = merged;
|
|
482
|
+
}
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
353
487
|
const lengthBefore = roomMessages.length;
|
|
354
488
|
|
|
355
489
|
if (roomMessages.length === 0 || start) {
|
|
@@ -666,6 +800,16 @@ const reducers = {
|
|
|
666
800
|
}
|
|
667
801
|
}
|
|
668
802
|
},
|
|
803
|
+
// Merges resolved room members into the identity cache Message.tsx /
|
|
804
|
+
// resolveSenderDisplayName read from. Keyed by BOTH the bare local
|
|
805
|
+
// part and the full jid so either lookup style hits — a message's
|
|
806
|
+
// `user.id` can be either depending on how it was parsed.
|
|
807
|
+
mergeUsersSet(
|
|
808
|
+
state: WritableDraft<RoomMessagesState>,
|
|
809
|
+
action: PayloadAction<{ members: Record<string, any> }>
|
|
810
|
+
) {
|
|
811
|
+
state.usersSet = { ...(state.usersSet || {}), ...action.payload.members };
|
|
812
|
+
},
|
|
669
813
|
};
|
|
670
814
|
|
|
671
815
|
export const roomsStore: Slice<RoomMessagesState, typeof reducers, 'roomMessages'> = createSlice({
|
|
@@ -770,6 +914,7 @@ export const {
|
|
|
770
914
|
setPendingNotificationJid,
|
|
771
915
|
clearPendingNotificationJid,
|
|
772
916
|
setReactions,
|
|
917
|
+
mergeUsersSet,
|
|
773
918
|
} = roomsStore.actions;
|
|
774
919
|
|
|
775
920
|
export default roomsStore.reducer;
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
// Regional variants for the three languages LANGUAGE_OPTIONS currently
|
|
2
|
+
// offers, matching the deployment's actual reader regions: Canadian
|
|
3
|
+
// English/French, US Spanish. Bare 2-letter codes stay valid alongside
|
|
4
|
+
// them (toBaseLanguage reduces either form to the same base for i18n
|
|
5
|
+
// string-table lookups and translation-language comparisons), since other
|
|
6
|
+
// flows (config.translates.translations) still produce or accept the bare
|
|
7
|
+
// form.
|
|
8
|
+
export type Iso639_1Codes =
|
|
9
|
+
| 'en'
|
|
10
|
+
| 'en-CA'
|
|
11
|
+
| 'es'
|
|
12
|
+
| 'es-US'
|
|
13
|
+
| 'pt'
|
|
14
|
+
| 'ht'
|
|
15
|
+
| 'fr'
|
|
16
|
+
| 'fr-CA'
|
|
17
|
+
| 'zh';
|
|
2
18
|
|
|
3
19
|
export interface Language {
|
|
4
20
|
iso639_1: Iso639_1Codes;
|
|
@@ -36,6 +36,15 @@ export interface IMessage {
|
|
|
36
36
|
// Set once a message has been corrected via XEP-0308 (<replace>), so the
|
|
37
37
|
// bubble can show a small "edited" marker (Slack/WhatsApp style).
|
|
38
38
|
isEdited?: boolean;
|
|
39
|
+
// Present when this message is a call-log entry derived from a
|
|
40
|
+
// `<data type="call-state">` stanza (see helpers/callLogMessage.ts).
|
|
41
|
+
callLog?: {
|
|
42
|
+
callId: string;
|
|
43
|
+
direction: 'outgoing' | 'incoming';
|
|
44
|
+
durationMs: number;
|
|
45
|
+
missed: boolean;
|
|
46
|
+
kind: 'audio' | 'video';
|
|
47
|
+
};
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
export interface ReactionMessage {
|
package/src/types/types.ts
CHANGED
|
@@ -401,7 +401,7 @@ export interface IConfig {
|
|
|
401
401
|
* When `true`, an idle input (no text, no attachments) shows a
|
|
402
402
|
* microphone in the send-button slot; tap → start recording → stop &
|
|
403
403
|
* send. iOS apps need `NSMicrophoneUsageDescription` in Info.plist
|
|
404
|
-
* (add via `expo-
|
|
404
|
+
* (add via `expo-audio`'s plugin block in `app.json`). Receiving voice
|
|
405
405
|
* messages from other clients (incl. legacy web `.bin` voicemails) is
|
|
406
406
|
* independent of this flag — incoming audio plays regardless.
|
|
407
407
|
*/
|
|
@@ -594,13 +594,96 @@ export interface IConfig {
|
|
|
594
594
|
customSystemMessage?: React.ComponentType<MessageProps>;
|
|
595
595
|
|
|
596
596
|
// ----- translations -----
|
|
597
|
-
translates?: {
|
|
597
|
+
translates?: {
|
|
598
|
+
enabled: boolean;
|
|
599
|
+
translations?: Iso639_1Codes;
|
|
600
|
+
/**
|
|
601
|
+
* 'auto' shows the translation inline automatically. 'manual' shows a
|
|
602
|
+
* "Translate" link the reader taps (LinkedIn-style), then renders the
|
|
603
|
+
* result inline with a "Show original" toggle. Default 'auto'.
|
|
604
|
+
*
|
|
605
|
+
* Neither mode calls any translation service, both only ever display
|
|
606
|
+
* `message.translations`, whatever arrived attached to the stanza (see
|
|
607
|
+
* `readerLocale` below for how a message gets translated in the first
|
|
608
|
+
* place). This is purely a display choice: show it immediately, or let
|
|
609
|
+
* the reader ask for it.
|
|
610
|
+
*/
|
|
611
|
+
mode?: 'auto' | 'manual';
|
|
612
|
+
/**
|
|
613
|
+
* Pins `mode` as fixed host policy. When true, the language-selector
|
|
614
|
+
* modal's auto/manual switcher does not render at all, and the reader
|
|
615
|
+
* has no way to override `mode`, it's always whatever the host set.
|
|
616
|
+
* Defaults to false: the reader can flip between auto and manual
|
|
617
|
+
* themselves via the switcher, which then wins over `mode` (the host's
|
|
618
|
+
* declared default, used until the reader touches it).
|
|
619
|
+
*/
|
|
620
|
+
forceType?: boolean;
|
|
621
|
+
/**
|
|
622
|
+
* Reader's full locale (BCP-47, e.g. "fr-CA"). Falls back to
|
|
623
|
+
* `config.i18n.locale`. The region is passed through to `onTranslate` so
|
|
624
|
+
* the service can distinguish fr-CA vs fr-FR; the Translate button
|
|
625
|
+
* visibility comparison ignores the region (en-US vs en-CA => no button).
|
|
626
|
+
*
|
|
627
|
+
* This is also how a host drives the reader's language from OUTSIDE the
|
|
628
|
+
* chat component: set it (e.g. from your own app's language switcher)
|
|
629
|
+
* and pass the updated config down as a normal prop.
|
|
630
|
+
*/
|
|
631
|
+
readerLocale?: string;
|
|
632
|
+
/**
|
|
633
|
+
* Shows/hides the globe-icon language picker in the chat header.
|
|
634
|
+
* Defaults to true whenever `enabled` is true. Set to false when the
|
|
635
|
+
* host manages the reader's language itself (via `readerLocale`) and
|
|
636
|
+
* an in-chat picker would just be a second, redundant control.
|
|
637
|
+
*/
|
|
638
|
+
showLanguageSelector?: boolean;
|
|
639
|
+
/**
|
|
640
|
+
* Shows/hides the list of selectable languages INSIDE the picker
|
|
641
|
+
* (English/Espanol/...). Defaults to true. Set to false to keep only
|
|
642
|
+
* the enable/disable-translates toggle.
|
|
643
|
+
*/
|
|
644
|
+
showLanguageList?: boolean;
|
|
645
|
+
/**
|
|
646
|
+
* Host-provided translation function. When set, the manual Translate
|
|
647
|
+
* action calls this, wire it to your own service, instead of reading
|
|
648
|
+
* `message.translations`. Optional; omit to use only what already
|
|
649
|
+
* arrived over XMPP.
|
|
650
|
+
*/
|
|
651
|
+
onTranslate?: (
|
|
652
|
+
text: string,
|
|
653
|
+
ctx: { sourceLocale?: string; targetLocale: string; message: IMessage }
|
|
654
|
+
) => Promise<string>;
|
|
655
|
+
/**
|
|
656
|
+
* Host predicate deciding whether to show the Translate action for a
|
|
657
|
+
* given message. When omitted, the component compares base languages
|
|
658
|
+
* (message source vs reader, region ignored) and shows the action when
|
|
659
|
+
* they differ.
|
|
660
|
+
*/
|
|
661
|
+
showTranslateForMessage?: (message: IMessage) => boolean;
|
|
662
|
+
};
|
|
663
|
+
/**
|
|
664
|
+
* Static UI i18n (interface captions like "Search...", "Type message").
|
|
665
|
+
* `locale` is a BCP-47 tag the host passes from the device/user (e.g. "en",
|
|
666
|
+
* "fr-CA", "es-US"); captions resolve to its base language. `strings`
|
|
667
|
+
* overrides or extends any built-in caption by key (see src/i18n/strings.ts).
|
|
668
|
+
* Built-in languages: en, fr, es, pt, ht, zh. Independent from `translates`
|
|
669
|
+
* (dynamic per-message translation).
|
|
670
|
+
*/
|
|
671
|
+
i18n?: {
|
|
672
|
+
locale?: string;
|
|
673
|
+
strings?: Record<string, string>;
|
|
674
|
+
};
|
|
598
675
|
enableTranslates?: boolean;
|
|
599
676
|
|
|
600
677
|
// ----- notifications -----
|
|
601
678
|
inAppNotifications?: InAppNotificationConfig;
|
|
602
679
|
pushNotifications?: {
|
|
603
680
|
enabled?: boolean;
|
|
681
|
+
/**
|
|
682
|
+
* Base URL of the push subscription service, including the version
|
|
683
|
+
* segment (e.g. "https://push.chat.ethora.com/api/v1"). Set this for
|
|
684
|
+
* self-hosted or enterprise deployments; omit to use Ethora's.
|
|
685
|
+
*/
|
|
686
|
+
apiUrl?: string;
|
|
604
687
|
iconPath?: string;
|
|
605
688
|
badgePath?: string;
|
|
606
689
|
onClick?: (params: {
|
|
@@ -640,6 +723,63 @@ export interface IConfig {
|
|
|
640
723
|
messageType: 'text' | 'media';
|
|
641
724
|
}) => void;
|
|
642
725
|
};
|
|
726
|
+
|
|
727
|
+
// ----- audio / video calls -----
|
|
728
|
+
/**
|
|
729
|
+
* LiveKit-backed 1:1 audio and video calls. Off unless `enabled` is
|
|
730
|
+
* explicitly `true`. Requires the native peer dependencies
|
|
731
|
+
* `@livekit/react-native` and `@livekit/react-native-webrtc`, plus
|
|
732
|
+
* microphone/camera permissions and the background audio modes listed
|
|
733
|
+
* in the README. Expo Go cannot run these, the host app needs a dev
|
|
734
|
+
* build or a prebuilt binary.
|
|
735
|
+
*/
|
|
736
|
+
videoCalls?: VideoCallsConfig;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Override the icons used by the in-call control bar and the incoming-call
|
|
741
|
+
* ring screen. Each value is any React node. When omitted, the chat's
|
|
742
|
+
* built-in icon is used. The mic/camera/screen-share toggles take separate
|
|
743
|
+
* on/off icons so the host can express active vs muted however they like.
|
|
744
|
+
*/
|
|
745
|
+
export interface VideoCallIcons {
|
|
746
|
+
micOn?: React.ReactNode;
|
|
747
|
+
micOff?: React.ReactNode;
|
|
748
|
+
cameraOn?: React.ReactNode;
|
|
749
|
+
cameraOff?: React.ReactNode;
|
|
750
|
+
screenShareOn?: React.ReactNode;
|
|
751
|
+
screenShareOff?: React.ReactNode;
|
|
752
|
+
hangup?: React.ReactNode;
|
|
753
|
+
accept?: React.ReactNode;
|
|
754
|
+
decline?: React.ReactNode;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
export interface VideoCallsConfig {
|
|
758
|
+
enabled: boolean;
|
|
759
|
+
livekitUrl: string;
|
|
760
|
+
allowedRoomTypes?: Array<'private'>;
|
|
761
|
+
/**
|
|
762
|
+
* Show the audio-only call button next to the video-call button. Off by
|
|
763
|
+
* default: it's a separate opt-in feature. An audio call uses the same
|
|
764
|
+
* call backend as video (no server changes needed); the client just
|
|
765
|
+
* starts the session with `kind: 'audio'` so no camera track is
|
|
766
|
+
* published, and the callee learns the kind from the direct
|
|
767
|
+
* `call-invite` signal. Requires `enabled` and a `livekitUrl` like
|
|
768
|
+
* video calls.
|
|
769
|
+
*/
|
|
770
|
+
enableAudioCalls?: boolean;
|
|
771
|
+
/** Start a video call with the camera already on. Default true. */
|
|
772
|
+
startWithCameraOn?: boolean;
|
|
773
|
+
/** Start a call with the microphone already on. Default true. */
|
|
774
|
+
startWithMicOn?: boolean;
|
|
775
|
+
/**
|
|
776
|
+
* Show the screen-share control. Default false on React Native: screen
|
|
777
|
+
* capture needs a foreground service on Android and a Broadcast Upload
|
|
778
|
+
* Extension on iOS, neither of which the SDK can add to a host app.
|
|
779
|
+
*/
|
|
780
|
+
showScreenShare?: boolean;
|
|
781
|
+
/** Custom icons for the call control bar / ring screen. */
|
|
782
|
+
icons?: VideoCallIcons;
|
|
643
783
|
}
|
|
644
784
|
|
|
645
785
|
interface ConfigRoom {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IConfig } from '../types/types';
|
|
2
|
+
|
|
3
|
+
export type TranslateMode = 'auto' | 'manual';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Whether the reader is allowed to pick auto vs manual themselves via the
|
|
7
|
+
* language-selector modal's switcher. `forceType: true` means the host has
|
|
8
|
+
* pinned `translates.mode` as policy, the switcher must not even render,
|
|
9
|
+
* let alone let a reader flip it.
|
|
10
|
+
*/
|
|
11
|
+
export const canReaderChooseTranslateMode = (
|
|
12
|
+
translates?: IConfig['translates']
|
|
13
|
+
): boolean => !translates?.forceType;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The mode actually in effect for this reader right now.
|
|
17
|
+
*
|
|
18
|
+
* `forceType` wins unconditionally, even a leftover `readerMode` from
|
|
19
|
+
* before the host turned forcing on must not leak through. Otherwise the
|
|
20
|
+
* reader's own pick (if they've touched the switcher) wins over the host's
|
|
21
|
+
* declared default, and the host's `mode` (defaulting to 'auto') is the
|
|
22
|
+
* fallback for a reader who never has.
|
|
23
|
+
*/
|
|
24
|
+
export const resolveTranslateMode = (
|
|
25
|
+
translates?: IConfig['translates'],
|
|
26
|
+
readerMode?: TranslateMode
|
|
27
|
+
): TranslateMode => {
|
|
28
|
+
const hostDefault: TranslateMode = translates?.mode || 'auto';
|
|
29
|
+
if (translates?.forceType) {return hostDefault;}
|
|
30
|
+
return readerMode || hostDefault;
|
|
31
|
+
};
|