@ethora/chat-component-rn 26.10.4 → 26.10.6
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 +8 -0
- package/README.md +15 -0
- package/app.plugin.js +2 -0
- package/lib/commonjs/assets/icons.js +2 -2
- package/lib/commonjs/assets/icons.js.map +1 -1
- package/lib/commonjs/components/MainComponents/AlbumGrid.js +152 -0
- package/lib/commonjs/components/MainComponents/AlbumGrid.js.map +1 -0
- package/lib/commonjs/components/MainComponents/ChatRoom.js +2 -2
- package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +199 -123
- package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js +109 -0
- package/lib/commonjs/components/MainComponents/MediaUploadOverlay.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/MediaCaption.js +51 -0
- package/lib/commonjs/components/MessageBubble/MediaCaption.js.map +1 -0
- package/lib/commonjs/components/MessageBubble/Message.js +40 -1
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +2 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/LastMessageItem.js +20 -4
- package/lib/commonjs/components/RoomComponents/LastMessageItem.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js +3 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/commonjs/components/Thread/ThreadWrapper.js +3 -2
- package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js +61 -16
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/CallIcons.js +41 -16
- package/lib/commonjs/components/VideoCalls/CallIcons.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +183 -9
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js +40 -15
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callSessionController.js +49 -26
- package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callUiStore.js +33 -4
- package/lib/commonjs/components/VideoCalls/callUiStore.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js +9 -1
- package/lib/commonjs/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useCallKeep.js +197 -28
- package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js +88 -0
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/usePipTile.js +232 -0
- package/lib/commonjs/components/VideoCalls/usePipTile.js.map +1 -0
- package/lib/commonjs/components/styled/MediaFilePreview.js +60 -47
- package/lib/commonjs/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/commonjs/components/styled/MessageImage.js +16 -6
- package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +99 -68
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/components/styled/VideoMessage.js +24 -10
- package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +46 -0
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/e2ee/fileEnvelope.js +6 -0
- package/lib/commonjs/e2ee/fileEnvelope.js.map +1 -1
- package/lib/commonjs/e2ee/sealedBody.js +32 -2
- package/lib/commonjs/e2ee/sealedBody.js.map +1 -1
- package/lib/commonjs/helpers/albumLayout.js +150 -0
- package/lib/commonjs/helpers/albumLayout.js.map +1 -0
- package/lib/commonjs/helpers/attachments.js +255 -0
- package/lib/commonjs/helpers/attachments.js.map +1 -0
- package/lib/commonjs/helpers/caption.js +214 -0
- package/lib/commonjs/helpers/caption.js.map +1 -0
- package/lib/commonjs/helpers/createMainMessageForThread.js +5 -1
- package/lib/commonjs/helpers/createMainMessageForThread.js.map +1 -1
- package/lib/commonjs/helpers/createMessageFromXml.js +19 -5
- package/lib/commonjs/helpers/createMessageFromXml.js.map +1 -1
- package/lib/commonjs/helpers/fileKind.js +40 -0
- package/lib/commonjs/helpers/fileKind.js.map +1 -0
- package/lib/commonjs/helpers/getDataFromXml.js +6 -1
- package/lib/commonjs/helpers/getDataFromXml.js.map +1 -1
- package/lib/commonjs/helpers/mapWithConcurrency.js +41 -0
- package/lib/commonjs/helpers/mapWithConcurrency.js.map +1 -0
- package/lib/commonjs/helpers/mediaLimits.js +33 -0
- package/lib/commonjs/helpers/mediaLimits.js.map +1 -0
- package/lib/commonjs/helpers/mediaSendRetry.js +64 -0
- package/lib/commonjs/helpers/mediaSendRetry.js.map +1 -0
- package/lib/commonjs/helpers/mediaWireData.js +62 -0
- package/lib/commonjs/helpers/mediaWireData.js.map +1 -0
- package/lib/commonjs/helpers/mentionSpans.js +19 -0
- package/lib/commonjs/helpers/mentionSpans.js.map +1 -0
- package/lib/commonjs/helpers/mimeToExtension.js +45 -0
- package/lib/commonjs/helpers/mimeToExtension.js.map +1 -1
- package/lib/commonjs/helpers/readReceipts.js +173 -0
- package/lib/commonjs/helpers/readReceipts.js.map +1 -0
- package/lib/commonjs/helpers/repairFilenameEncoding.js +79 -0
- package/lib/commonjs/helpers/repairFilenameEncoding.js.map +1 -0
- package/lib/commonjs/helpers/safeJson.js +18 -0
- package/lib/commonjs/helpers/safeJson.js.map +1 -0
- package/lib/commonjs/helpers/updateMessagesTillLast.js +3 -1
- package/lib/commonjs/helpers/updateMessagesTillLast.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +384 -193
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/i18n/strings.js +144 -0
- package/lib/commonjs/i18n/strings.js.map +1 -1
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/api-requests/auth.api.js +62 -3
- package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js +100 -3
- package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js +4 -2
- package/lib/commonjs/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js +28 -0
- package/lib/commonjs/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
- package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
- package/lib/commonjs/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js +28 -0
- package/lib/commonjs/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
- package/lib/commonjs/networking/xmppClient.js +64 -0
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/persistence.js +78 -5
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/roomStore/roomHeapSlice.js +4 -0
- package/lib/commonjs/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +114 -1
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/commonjs/types/models/attachment.model.js +2 -0
- package/lib/commonjs/types/models/attachment.model.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/assets/icons.js +1 -1
- package/lib/module/assets/icons.js.map +1 -1
- package/lib/module/components/MainComponents/AlbumGrid.js +146 -0
- package/lib/module/components/MainComponents/AlbumGrid.js.map +1 -0
- package/lib/module/components/MainComponents/ChatRoom.js +2 -2
- package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +200 -124
- package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/module/components/MainComponents/MediaUploadOverlay.js +104 -0
- package/lib/module/components/MainComponents/MediaUploadOverlay.js.map +1 -0
- package/lib/module/components/MessageBubble/MediaCaption.js +46 -0
- package/lib/module/components/MessageBubble/MediaCaption.js.map +1 -0
- package/lib/module/components/MessageBubble/Message.js +44 -5
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +2 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +75 -0
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/RoomComponents/LastMessageItem.js +20 -4
- package/lib/module/components/RoomComponents/LastMessageItem.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastAudioMessage.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastAudioMessage.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js +3 -2
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/module/components/Thread/ThreadWrapper.js +3 -2
- package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/module/components/VideoCalls/ActiveCallBar.js +63 -18
- package/lib/module/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/module/components/VideoCalls/CallIcons.js +38 -15
- package/lib/module/components/VideoCalls/CallIcons.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +185 -11
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallSession.js +41 -16
- package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/module/components/VideoCalls/callSessionController.js +46 -24
- package/lib/module/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/module/components/VideoCalls/callUiStore.js +30 -3
- package/lib/module/components/VideoCalls/callUiStore.js.map +1 -1
- package/lib/module/components/VideoCalls/livekitRuntime.js +9 -1
- package/lib/module/components/VideoCalls/livekitRuntime.js.map +1 -1
- package/lib/module/components/VideoCalls/useCallKeep.js +198 -29
- package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js +84 -0
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js.map +1 -0
- package/lib/module/components/VideoCalls/usePipTile.js +228 -0
- package/lib/module/components/VideoCalls/usePipTile.js.map +1 -0
- package/lib/module/components/styled/MediaFilePreview.js +61 -48
- package/lib/module/components/styled/MediaFilePreview.js.map +1 -1
- package/lib/module/components/styled/MessageImage.js +16 -6
- package/lib/module/components/styled/MessageImage.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +99 -68
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/components/styled/VideoMessage.js +24 -10
- package/lib/module/components/styled/VideoMessage.js.map +1 -1
- package/lib/module/context/xmppProvider.js +46 -0
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/e2ee/fileEnvelope.js +1 -1
- package/lib/module/e2ee/fileEnvelope.js.map +1 -1
- package/lib/module/e2ee/sealedBody.js +32 -2
- package/lib/module/e2ee/sealedBody.js.map +1 -1
- package/lib/module/helpers/albumLayout.js +143 -0
- package/lib/module/helpers/albumLayout.js.map +1 -0
- package/lib/module/helpers/attachments.js +245 -0
- package/lib/module/helpers/attachments.js.map +1 -0
- package/lib/module/helpers/caption.js +207 -0
- package/lib/module/helpers/caption.js.map +1 -0
- package/lib/module/helpers/createMainMessageForThread.js +5 -1
- package/lib/module/helpers/createMainMessageForThread.js.map +1 -1
- package/lib/module/helpers/createMessageFromXml.js +20 -6
- package/lib/module/helpers/createMessageFromXml.js.map +1 -1
- package/lib/module/helpers/fileKind.js +34 -0
- package/lib/module/helpers/fileKind.js.map +1 -0
- package/lib/module/helpers/getDataFromXml.js +6 -1
- package/lib/module/helpers/getDataFromXml.js.map +1 -1
- package/lib/module/helpers/mapWithConcurrency.js +37 -0
- package/lib/module/helpers/mapWithConcurrency.js.map +1 -0
- package/lib/module/helpers/mediaLimits.js +27 -0
- package/lib/module/helpers/mediaLimits.js.map +1 -0
- package/lib/module/helpers/mediaSendRetry.js +55 -0
- package/lib/module/helpers/mediaSendRetry.js.map +1 -0
- package/lib/module/helpers/mediaWireData.js +58 -0
- package/lib/module/helpers/mediaWireData.js.map +1 -0
- package/lib/module/helpers/mentionSpans.js +14 -0
- package/lib/module/helpers/mentionSpans.js.map +1 -0
- package/lib/module/helpers/mimeToExtension.js +43 -0
- package/lib/module/helpers/mimeToExtension.js.map +1 -1
- package/lib/module/helpers/readReceipts.js +160 -0
- package/lib/module/helpers/readReceipts.js.map +1 -0
- package/lib/module/helpers/repairFilenameEncoding.js +74 -0
- package/lib/module/helpers/repairFilenameEncoding.js.map +1 -0
- package/lib/module/helpers/safeJson.js +13 -0
- package/lib/module/helpers/safeJson.js.map +1 -0
- package/lib/module/helpers/updateMessagesTillLast.js +3 -1
- package/lib/module/helpers/updateMessagesTillLast.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +387 -195
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/i18n/strings.js +144 -0
- package/lib/module/i18n/strings.js.map +1 -1
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/api-requests/auth.api.js +62 -3
- package/lib/module/networking/api-requests/auth.api.js.map +1 -1
- package/lib/module/networking/stanzaHandlers.js +97 -4
- package/lib/module/networking/stanzaHandlers.js.map +1 -1
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js +5 -3
- package/lib/module/networking/xmpp/handleStanzas.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js +25 -0
- package/lib/module/networking/xmpp/sendDisplayedMarker.xmpp.js.map +1 -0
- package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js +53 -2
- package/lib/module/networking/xmpp/sendMediaMessage.xmpp.js.map +1 -1
- package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js +25 -0
- package/lib/module/networking/xmpp/sendReceivedReceipt.xmpp.js.map +1 -0
- package/lib/module/networking/xmppClient.js +64 -0
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/persistence.js +78 -5
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/roomStore/roomHeapSlice.js +4 -0
- package/lib/module/roomStore/roomHeapSlice.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +109 -0
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/types/models/attachment.model.js +2 -0
- package/lib/module/types/models/attachment.model.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/assets/icons.d.ts +6 -1
- package/lib/typescript/assets/icons.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/AlbumGrid.d.ts +21 -0
- package/lib/typescript/components/MainComponents/AlbumGrid.d.ts.map +1 -0
- package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts +17 -0
- package/lib/typescript/components/MainComponents/MediaUploadOverlay.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble/MediaCaption.d.ts +12 -0
- package/lib/typescript/components/MessageBubble/MediaCaption.d.ts.map +1 -0
- package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/LastMessageItem.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastAudioMessage.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts +2 -0
- package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts.map +1 -1
- package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts +9 -3
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts +9 -0
- package/lib/typescript/components/VideoCalls/CallIcons.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts +7 -0
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts +13 -0
- package/lib/typescript/components/VideoCalls/callUiStore.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts +5 -0
- package/lib/typescript/components/VideoCalls/livekitRuntime.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts +12 -0
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts +10 -0
- package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/usePipTile.d.ts +46 -0
- package/lib/typescript/components/VideoCalls/usePipTile.d.ts.map +1 -0
- package/lib/typescript/components/styled/MediaFilePreview.d.ts.map +1 -1
- package/lib/typescript/components/styled/MessageImage.d.ts +10 -0
- package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts +7 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/components/styled/VideoMessage.d.ts +7 -0
- package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/e2ee/fileEnvelope.d.ts +1 -1
- package/lib/typescript/e2ee/fileEnvelope.d.ts.map +1 -1
- package/lib/typescript/e2ee/sealedBody.d.ts +16 -0
- package/lib/typescript/e2ee/sealedBody.d.ts.map +1 -1
- package/lib/typescript/helpers/albumLayout.d.ts +66 -0
- package/lib/typescript/helpers/albumLayout.d.ts.map +1 -0
- package/lib/typescript/helpers/attachments.d.ts +55 -0
- package/lib/typescript/helpers/attachments.d.ts.map +1 -0
- package/lib/typescript/helpers/caption.d.ts +64 -0
- package/lib/typescript/helpers/caption.d.ts.map +1 -0
- package/lib/typescript/helpers/createMainMessageForThread.d.ts.map +1 -1
- package/lib/typescript/helpers/createMessageFromXml.d.ts.map +1 -1
- package/lib/typescript/helpers/fileKind.d.ts +20 -0
- package/lib/typescript/helpers/fileKind.d.ts.map +1 -0
- package/lib/typescript/helpers/getDataFromXml.d.ts.map +1 -1
- package/lib/typescript/helpers/mapWithConcurrency.d.ts +9 -0
- package/lib/typescript/helpers/mapWithConcurrency.d.ts.map +1 -0
- package/lib/typescript/helpers/mediaLimits.d.ts +13 -0
- package/lib/typescript/helpers/mediaLimits.d.ts.map +1 -0
- package/lib/typescript/helpers/mediaSendRetry.d.ts +26 -0
- package/lib/typescript/helpers/mediaSendRetry.d.ts.map +1 -0
- package/lib/typescript/helpers/mediaWireData.d.ts +16 -0
- package/lib/typescript/helpers/mediaWireData.d.ts.map +1 -0
- package/lib/typescript/helpers/mentionSpans.d.ts +8 -0
- package/lib/typescript/helpers/mentionSpans.d.ts.map +1 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts +7 -0
- package/lib/typescript/helpers/mimeToExtension.d.ts.map +1 -1
- package/lib/typescript/helpers/readReceipts.d.ts +82 -0
- package/lib/typescript/helpers/readReceipts.d.ts.map +1 -0
- package/lib/typescript/helpers/repairFilenameEncoding.d.ts +12 -0
- package/lib/typescript/helpers/repairFilenameEncoding.d.ts.map +1 -0
- package/lib/typescript/helpers/safeJson.d.ts +2 -0
- package/lib/typescript/helpers/safeJson.d.ts.map +1 -0
- package/lib/typescript/helpers/updateMessagesTillLast.d.ts.map +1 -1
- package/lib/typescript/hooks/useSendMessage.d.ts +2 -1
- package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/i18n/strings.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +1 -1
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/auth.api.d.ts +2 -0
- package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
- package/lib/typescript/networking/stanzaHandlers.d.ts +7 -3
- 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/sendDisplayedMarker.xmpp.d.ts +3 -0
- package/lib/typescript/networking/xmpp/sendDisplayedMarker.xmpp.d.ts.map +1 -0
- package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts +12 -0
- package/lib/typescript/networking/xmpp/sendMediaMessage.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts +3 -0
- package/lib/typescript/networking/xmpp/sendReceivedReceipt.xmpp.d.ts.map +1 -0
- package/lib/typescript/networking/xmppClient.d.ts +15 -0
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomHeapSlice.d.ts +22 -1
- package/lib/typescript/roomStore/roomHeapSlice.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts +67 -3
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/types/models/attachment.model.d.ts +52 -0
- package/lib/typescript/types/models/attachment.model.d.ts.map +1 -0
- package/lib/typescript/types/models/message.model.d.ts +26 -0
- package/lib/typescript/types/models/message.model.d.ts.map +1 -1
- package/lib/typescript/types/models/modal.model.d.ts +7 -0
- package/lib/typescript/types/models/modal.model.d.ts.map +1 -1
- package/lib/typescript/types/models/room.model.d.ts +2 -0
- package/lib/typescript/types/models/room.model.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +69 -1
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +4 -1
- package/plugins/notification-service/Info.plist +31 -0
- package/plugins/notification-service/NotificationService.swift +82 -0
- package/plugins/withNotificationServiceExtension.js +137 -0
- package/src/assets/icons.tsx +20 -2
- package/src/components/MainComponents/AlbumGrid.tsx +183 -0
- package/src/components/MainComponents/ChatRoom.tsx +4 -2
- package/src/components/MainComponents/MediaMessage.tsx +246 -130
- package/src/components/MainComponents/MediaUploadOverlay.tsx +102 -0
- package/src/components/MessageBubble/MediaCaption.tsx +45 -0
- package/src/components/MessageBubble/Message.tsx +71 -3
- package/src/components/MessageBubble/MessageInteractions.tsx +2 -1
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +64 -0
- package/src/components/RoomComponents/LastMessageItem.tsx +15 -4
- package/src/components/RoomComponents/styled/LastAudioMessage.tsx +6 -3
- package/src/components/RoomComponents/styled/LastMessageFile.tsx +6 -2
- package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +6 -2
- package/src/components/RoomComponents/styled/LastMessageVideo.tsx +6 -2
- package/src/components/Thread/ThreadWrapper.tsx +5 -9
- package/src/components/VideoCalls/ActiveCallBar.tsx +56 -10
- package/src/components/VideoCalls/CallIcons.tsx +36 -12
- package/src/components/VideoCalls/VideoCallOverlay.tsx +205 -8
- package/src/components/VideoCalls/VideoCallSession.tsx +45 -13
- package/src/components/VideoCalls/callSessionController.ts +51 -20
- package/src/components/VideoCalls/callUiStore.ts +33 -3
- package/src/components/VideoCalls/livekitRuntime.ts +13 -0
- package/src/components/VideoCalls/useCallKeep.ts +204 -33
- package/src/components/VideoCalls/useOngoingCallNotification.ts +113 -0
- package/src/components/VideoCalls/usePipTile.ts +214 -0
- package/src/components/styled/MediaFilePreview.tsx +49 -34
- package/src/components/styled/MessageImage.tsx +21 -5
- package/src/components/styled/SendInput.tsx +101 -87
- package/src/components/styled/VideoMessage.tsx +34 -9
- package/src/context/xmppProvider.tsx +32 -0
- package/src/e2ee/fileEnvelope.ts +1 -1
- package/src/e2ee/sealedBody.ts +44 -5
- package/src/helpers/albumLayout.ts +207 -0
- package/src/helpers/attachments.ts +316 -0
- package/src/helpers/caption.ts +283 -0
- package/src/helpers/createMainMessageForThread.ts +6 -1
- package/src/helpers/createMessageFromXml.ts +21 -5
- package/src/helpers/fileKind.ts +38 -0
- package/src/helpers/getDataFromXml.ts +5 -1
- package/src/helpers/mapWithConcurrency.ts +32 -0
- package/src/helpers/mediaLimits.ts +27 -0
- package/src/helpers/mediaSendRetry.ts +59 -0
- package/src/helpers/mediaWireData.ts +57 -0
- package/src/helpers/mentionSpans.ts +17 -0
- package/src/helpers/mimeToExtension.ts +51 -0
- package/src/helpers/readReceipts.ts +219 -0
- package/src/helpers/repairFilenameEncoding.ts +82 -0
- package/src/helpers/safeJson.ts +11 -0
- package/src/helpers/updateMessagesTillLast.tsx +2 -1
- package/src/hooks/useSendMessage.tsx +448 -249
- package/src/i18n/strings.ts +144 -0
- package/src/main.ts +3 -0
- package/src/networking/api-requests/auth.api.ts +60 -3
- package/src/networking/stanzaHandlers.ts +91 -2
- package/src/networking/xmpp/handleStanzas.xmpp.ts +6 -2
- package/src/networking/xmpp/sendDisplayedMarker.xmpp.ts +31 -0
- package/src/networking/xmpp/sendMediaMessage.xmpp.ts +60 -3
- package/src/networking/xmpp/sendReceivedReceipt.xmpp.ts +31 -0
- package/src/networking/xmppClient.ts +48 -0
- package/src/roomStore/persistence.ts +94 -3
- package/src/roomStore/roomHeapSlice.ts +23 -1
- package/src/roomStore/roomsSlice.ts +112 -1
- package/src/types/models/attachment.model.ts +51 -0
- package/src/types/models/message.model.ts +29 -0
- package/src/types/models/modal.model.ts +7 -0
- package/src/types/models/room.model.ts +2 -0
- package/src/types/types.ts +83 -1
- package/src/version.ts +1 -1
- package/lib/commonjs/components/VideoCalls/useDraggable.js +0 -59
- package/lib/commonjs/components/VideoCalls/useDraggable.js.map +0 -1
- package/lib/module/components/VideoCalls/useDraggable.js +0 -55
- package/lib/module/components/VideoCalls/useDraggable.js.map +0 -1
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts +0 -19
- package/lib/typescript/components/VideoCalls/useDraggable.d.ts.map +0 -1
- package/src/components/VideoCalls/useDraggable.ts +0 -46
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { Animated, LayoutChangeEvent, PanResponder } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The small video tile of a call, Telegram-style: drag it anywhere and it
|
|
6
|
+
* snaps to the nearest corner when released (a flick carries it towards
|
|
7
|
+
* the flick), pinch it to switch between a small and a large size.
|
|
8
|
+
*
|
|
9
|
+
* Positions are kept per corner rather than per pixel, so the tile stays
|
|
10
|
+
* glued to its corner when the stage changes size (rotation, iPad split
|
|
11
|
+
* view) or the tile itself changes size.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type PipTileSize = 'small' | 'large';
|
|
15
|
+
|
|
16
|
+
export const PIP_TILE_SIZES: Record<PipTileSize, { width: number; height: number }> = {
|
|
17
|
+
small: { width: 104, height: 148 },
|
|
18
|
+
large: { width: 156, height: 222 },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const MARGIN = 12;
|
|
22
|
+
// How far a flick carries the tile before picking a corner, in ms of velocity.
|
|
23
|
+
const FLICK_PROJECTION_MS = 160;
|
|
24
|
+
// Pinch past these ratios to change size; in between keeps the current one.
|
|
25
|
+
const GROW_RATIO = 1.12;
|
|
26
|
+
const SHRINK_RATIO = 0.9;
|
|
27
|
+
|
|
28
|
+
interface Corner {
|
|
29
|
+
right: boolean;
|
|
30
|
+
bottom: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Stage {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const SPRING = { useNativeDriver: false, damping: 20, stiffness: 220, mass: 0.8 };
|
|
39
|
+
|
|
40
|
+
const touchDistance = (touches: ReadonlyArray<{ pageX: number; pageY: number }>) => {
|
|
41
|
+
const [a, b] = touches;
|
|
42
|
+
return Math.hypot(a.pageX - b.pageX, a.pageY - b.pageY);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const usePipTile = ({ topInset = 0 }: { topInset?: number } = {}) => {
|
|
46
|
+
const [stage, setStage] = useState<Stage | null>(null);
|
|
47
|
+
const [size, setSize] = useState<PipTileSize>('small');
|
|
48
|
+
const stageRef = useRef<Stage | null>(null);
|
|
49
|
+
const sizeRef = useRef<PipTileSize>('small');
|
|
50
|
+
const cornerRef = useRef<Corner>({ right: true, bottom: false });
|
|
51
|
+
const topInsetRef = useRef(topInset);
|
|
52
|
+
topInsetRef.current = topInset;
|
|
53
|
+
|
|
54
|
+
const position = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current;
|
|
55
|
+
const width = useRef(new Animated.Value(PIP_TILE_SIZES.small.width)).current;
|
|
56
|
+
const height = useRef(new Animated.Value(PIP_TILE_SIZES.small.height)).current;
|
|
57
|
+
const pinchScale = useRef(new Animated.Value(1)).current;
|
|
58
|
+
// Where the tile rests, i.e. where a drag starts from.
|
|
59
|
+
const restRef = useRef({ x: 0, y: 0 });
|
|
60
|
+
const gestureRef = useRef({ pinching: false, startDistance: 0, scale: 1 });
|
|
61
|
+
|
|
62
|
+
const cornerPoint = useCallback((corner: Corner, tileSize: PipTileSize) => {
|
|
63
|
+
const area = stageRef.current;
|
|
64
|
+
if (!area) {return { x: 0, y: 0 };}
|
|
65
|
+
const tile = PIP_TILE_SIZES[tileSize];
|
|
66
|
+
const top = MARGIN + topInsetRef.current;
|
|
67
|
+
return {
|
|
68
|
+
x: corner.right ? Math.max(MARGIN, area.width - tile.width - MARGIN) : MARGIN,
|
|
69
|
+
y: corner.bottom ? Math.max(top, area.height - tile.height - MARGIN) : top,
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const settle = useCallback(
|
|
74
|
+
(animated: boolean) => {
|
|
75
|
+
const tileSize = sizeRef.current;
|
|
76
|
+
const target = cornerPoint(cornerRef.current, tileSize);
|
|
77
|
+
const tile = PIP_TILE_SIZES[tileSize];
|
|
78
|
+
restRef.current = target;
|
|
79
|
+
if (!animated) {
|
|
80
|
+
position.setValue(target);
|
|
81
|
+
width.setValue(tile.width);
|
|
82
|
+
height.setValue(tile.height);
|
|
83
|
+
pinchScale.setValue(1);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
Animated.parallel([
|
|
87
|
+
Animated.spring(position, { ...SPRING, toValue: target }),
|
|
88
|
+
Animated.spring(width, { ...SPRING, toValue: tile.width }),
|
|
89
|
+
Animated.spring(height, { ...SPRING, toValue: tile.height }),
|
|
90
|
+
Animated.spring(pinchScale, { ...SPRING, toValue: 1 }),
|
|
91
|
+
]).start();
|
|
92
|
+
},
|
|
93
|
+
[cornerPoint, position, width, height, pinchScale]
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const onStageLayout = useCallback(
|
|
97
|
+
(event: LayoutChangeEvent) => {
|
|
98
|
+
const { width: w, height: h } = event.nativeEvent.layout;
|
|
99
|
+
const prev = stageRef.current;
|
|
100
|
+
if (prev && prev.width === w && prev.height === h) {return;}
|
|
101
|
+
stageRef.current = { width: w, height: h };
|
|
102
|
+
setStage(stageRef.current);
|
|
103
|
+
settle(false);
|
|
104
|
+
},
|
|
105
|
+
[settle]
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// The safe-area inset arrives after the first layout on some devices.
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (stageRef.current) {settle(false);}
|
|
111
|
+
}, [topInset, settle]);
|
|
112
|
+
|
|
113
|
+
const panHandlers = useMemo(
|
|
114
|
+
() =>
|
|
115
|
+
PanResponder.create({
|
|
116
|
+
// Two fingers are always ours (pinch); one finger only once it
|
|
117
|
+
// moves, so a tap still reaches the tile's onPress (swap videos).
|
|
118
|
+
onStartShouldSetPanResponderCapture: (event) =>
|
|
119
|
+
event.nativeEvent.touches.length >= 2,
|
|
120
|
+
// Own the touch from the start when the tile has no tap target
|
|
121
|
+
// (nothing to swap): an ancestor outside the call Modal would take
|
|
122
|
+
// it otherwise, and the move-capture below would never be asked.
|
|
123
|
+
onStartShouldSetPanResponder: () => true,
|
|
124
|
+
onMoveShouldSetPanResponderCapture: (event, gesture) =>
|
|
125
|
+
event.nativeEvent.touches.length >= 2 ||
|
|
126
|
+
Math.abs(gesture.dx) > 4 ||
|
|
127
|
+
Math.abs(gesture.dy) > 4,
|
|
128
|
+
// Never hand the tile over to the swipe-to-minimize of the screen.
|
|
129
|
+
onPanResponderTerminationRequest: () => false,
|
|
130
|
+
onPanResponderGrant: () => {
|
|
131
|
+
position.stopAnimation();
|
|
132
|
+
width.stopAnimation();
|
|
133
|
+
height.stopAnimation();
|
|
134
|
+
pinchScale.stopAnimation();
|
|
135
|
+
gestureRef.current = { pinching: false, startDistance: 0, scale: 1 };
|
|
136
|
+
},
|
|
137
|
+
onPanResponderMove: (event, gesture) => {
|
|
138
|
+
const touches = event.nativeEvent.touches;
|
|
139
|
+
const state = gestureRef.current;
|
|
140
|
+
if (touches.length >= 2) {
|
|
141
|
+
const distance = touchDistance(touches);
|
|
142
|
+
if (!state.pinching || state.startDistance <= 0) {
|
|
143
|
+
state.pinching = true;
|
|
144
|
+
state.startDistance = distance;
|
|
145
|
+
} else {
|
|
146
|
+
state.scale = Math.min(1.8, Math.max(0.6, distance / state.startDistance));
|
|
147
|
+
pinchScale.setValue(state.scale);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
position.setValue({
|
|
151
|
+
x: restRef.current.x + gesture.dx,
|
|
152
|
+
y: restRef.current.y + gesture.dy,
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
onPanResponderRelease: (_event, gesture) => {
|
|
156
|
+
const area = stageRef.current;
|
|
157
|
+
const state = gestureRef.current;
|
|
158
|
+
if (state.pinching) {
|
|
159
|
+
const next: PipTileSize =
|
|
160
|
+
state.scale >= GROW_RATIO
|
|
161
|
+
? 'large'
|
|
162
|
+
: state.scale <= SHRINK_RATIO
|
|
163
|
+
? 'small'
|
|
164
|
+
: sizeRef.current;
|
|
165
|
+
if (next !== sizeRef.current) {
|
|
166
|
+
// Keep the size the finger left it at, then let the spring
|
|
167
|
+
// carry it to the new one, so the tile doesn't jump.
|
|
168
|
+
const from = PIP_TILE_SIZES[sizeRef.current];
|
|
169
|
+
width.setValue(from.width * state.scale);
|
|
170
|
+
height.setValue(from.height * state.scale);
|
|
171
|
+
pinchScale.setValue(1);
|
|
172
|
+
sizeRef.current = next;
|
|
173
|
+
setSize(next);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (area) {
|
|
177
|
+
const tile = PIP_TILE_SIZES[sizeRef.current];
|
|
178
|
+
const centerX =
|
|
179
|
+
restRef.current.x + gesture.dx + gesture.vx * FLICK_PROJECTION_MS + tile.width / 2;
|
|
180
|
+
const centerY =
|
|
181
|
+
restRef.current.y + gesture.dy + gesture.vy * FLICK_PROJECTION_MS + tile.height / 2;
|
|
182
|
+
cornerRef.current = {
|
|
183
|
+
right: centerX > area.width / 2,
|
|
184
|
+
bottom: centerY > area.height / 2,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
settle(true);
|
|
188
|
+
},
|
|
189
|
+
onPanResponderTerminate: () => settle(true),
|
|
190
|
+
}).panHandlers,
|
|
191
|
+
[position, width, height, pinchScale, settle]
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
/** Put on the area the tile moves within. */
|
|
196
|
+
onStageLayout,
|
|
197
|
+
panHandlers,
|
|
198
|
+
size,
|
|
199
|
+
/** Hidden until the stage is measured, so it never flashes at 0,0. */
|
|
200
|
+
style: {
|
|
201
|
+
position: 'absolute' as const,
|
|
202
|
+
left: 0,
|
|
203
|
+
top: 0,
|
|
204
|
+
width,
|
|
205
|
+
height,
|
|
206
|
+
opacity: stage ? 1 : 0,
|
|
207
|
+
transform: [
|
|
208
|
+
{ translateX: position.x },
|
|
209
|
+
{ translateY: position.y },
|
|
210
|
+
{ scale: pinchScale },
|
|
211
|
+
],
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { View, Text, StyleSheet, Modal, TouchableOpacity, SafeAreaView } from 'react-native';
|
|
1
|
+
import { View, Text, StyleSheet, Modal, TouchableOpacity, SafeAreaView, ScrollView } from 'react-native';
|
|
2
2
|
import {
|
|
3
3
|
MediaContainer,
|
|
4
4
|
MediaImage,
|
|
@@ -8,6 +8,7 @@ import { RemoveButton, RemoveButtonText } from './StyledComponents';
|
|
|
8
8
|
import { FC, useState } from 'react';
|
|
9
9
|
import { MediaFile } from '../../types/types';
|
|
10
10
|
import { useVideoPlayer, VideoView } from 'expo-video';
|
|
11
|
+
import { useT } from '../../i18n/useT';
|
|
11
12
|
|
|
12
13
|
interface MediaFilePreviewProps {
|
|
13
14
|
filePreviews: MediaFile[];
|
|
@@ -62,42 +63,56 @@ export const MediaFilePreview: FC<MediaFilePreviewProps> = ({
|
|
|
62
63
|
handleRemoveImage,
|
|
63
64
|
}) => {
|
|
64
65
|
const [previewUri, setPreviewUri] = useState<string | null>(null);
|
|
66
|
+
const t = useT();
|
|
65
67
|
|
|
68
|
+
// The tray holds up to ten files, so it scrolls sideways rather than wrap.
|
|
66
69
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
70
|
+
<ScrollView
|
|
71
|
+
horizontal
|
|
72
|
+
showsHorizontalScrollIndicator={false}
|
|
73
|
+
keyboardShouldPersistTaps="handled"
|
|
74
|
+
testID="media-tray"
|
|
75
|
+
>
|
|
76
|
+
<MediaContainer>
|
|
77
|
+
{filePreviews.map((file, index) => {
|
|
78
|
+
const isImage = file.type?.startsWith('image');
|
|
79
|
+
const isVideo = file.type?.startsWith('video');
|
|
80
|
+
return (
|
|
81
|
+
<View key={`${file.name}_${index}`}>
|
|
82
|
+
{isImage ? (
|
|
83
|
+
<MediaImage source={{ uri: file.uri }} />
|
|
84
|
+
) : isVideo ? (
|
|
85
|
+
<VideoThumb uri={file.uri} onPress={() => setPreviewUri(file.uri)} />
|
|
86
|
+
) : (
|
|
87
|
+
<View style={{ width: 70, height: 70, borderRadius: 8 }}>
|
|
88
|
+
<FileIcon width={70} height={70} />
|
|
89
|
+
</View>
|
|
90
|
+
)}
|
|
91
|
+
<RemoveButton
|
|
92
|
+
testID={`media-tray-remove-${index}`}
|
|
93
|
+
accessibilityRole="button"
|
|
94
|
+
accessibilityLabel={t('attachment.remove')}
|
|
95
|
+
onPress={() => handleRemoveImage(index)}
|
|
96
|
+
>
|
|
97
|
+
<RemoveButtonText>×</RemoveButtonText>
|
|
98
|
+
</RemoveButton>
|
|
99
|
+
</View>
|
|
100
|
+
);
|
|
101
|
+
})}
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
<Modal visible={!!previewUri} animationType="fade" transparent>
|
|
104
|
+
<SafeAreaView style={styles.modalContainer}>
|
|
105
|
+
<TouchableOpacity
|
|
106
|
+
style={styles.closeButton}
|
|
107
|
+
onPress={() => setPreviewUri(null)}
|
|
108
|
+
>
|
|
109
|
+
<Text style={styles.closeText}>✕</Text>
|
|
110
|
+
</TouchableOpacity>
|
|
111
|
+
{previewUri && <FullscreenPreviewVideo uri={previewUri} />}
|
|
112
|
+
</SafeAreaView>
|
|
113
|
+
</Modal>
|
|
114
|
+
</MediaContainer>
|
|
115
|
+
</ScrollView>
|
|
101
116
|
);
|
|
102
117
|
};
|
|
103
118
|
|
|
@@ -36,6 +36,13 @@ interface CustomMessageImageProps {
|
|
|
36
36
|
* route; rendering it inline is what left the bubbles blank.
|
|
37
37
|
*/
|
|
38
38
|
locationPreview?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Album mode: fill a tile of exactly this size (cover, square corners; the
|
|
41
|
+
* tile clips) instead of sizing itself to the picture.
|
|
42
|
+
*/
|
|
43
|
+
tile?: { width: number; height: number };
|
|
44
|
+
/** Replaces the default "open this one file" tap, e.g. to open an album at an index. */
|
|
45
|
+
onOpen?: () => void;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
/** Keep the personal file token out of the logs. */
|
|
@@ -51,6 +58,8 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
51
58
|
originalName,
|
|
52
59
|
mimetype,
|
|
53
60
|
locationPreview,
|
|
61
|
+
tile,
|
|
62
|
+
onOpen,
|
|
54
63
|
}) => {
|
|
55
64
|
// Preview first, original only as a fallback — mirrors the web client,
|
|
56
65
|
// where the <img> src is the preview and `fileURL` is reserved for the
|
|
@@ -59,12 +68,13 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
59
68
|
const [loading, setLoading] = useState(true);
|
|
60
69
|
// Start with a neutral box; swap to the real aspect ratio once the
|
|
61
70
|
// image's natural dimensions are known so it's shown scaled, not cropped.
|
|
62
|
-
const [
|
|
71
|
+
const [measured, setDims] = useState<MediaDims>(defaultMediaDims());
|
|
72
|
+
const dims: MediaDims = tile ?? measured;
|
|
63
73
|
|
|
64
74
|
const dispatch = useDispatch();
|
|
65
75
|
|
|
66
76
|
useEffect(() => {
|
|
67
|
-
if (!displayURL) {
|
|
77
|
+
if (!displayURL || tile) {
|
|
68
78
|
return;
|
|
69
79
|
}
|
|
70
80
|
let active = true;
|
|
@@ -88,15 +98,21 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
88
98
|
return () => {
|
|
89
99
|
active = false;
|
|
90
100
|
};
|
|
91
|
-
}, [displayURL]);
|
|
101
|
+
}, [displayURL, tile]);
|
|
92
102
|
|
|
93
103
|
const handleOpen = () => {
|
|
104
|
+
if (onOpen) {
|
|
105
|
+
onOpen();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
94
108
|
dispatch(setActiveFile({ fileName, fileURL, mimetype, originalName }));
|
|
95
109
|
dispatch(setActiveModal(MODAL_TYPES.FILE_PREVIEW));
|
|
96
110
|
};
|
|
97
111
|
|
|
98
112
|
return (
|
|
99
|
-
<Container
|
|
113
|
+
<Container
|
|
114
|
+
style={tile ? { width: dims.width, borderRadius: 0 } : { width: dims.width }}
|
|
115
|
+
>
|
|
100
116
|
<TouchableOpacity onPress={handleOpen} activeOpacity={0.9}>
|
|
101
117
|
{loading && (
|
|
102
118
|
<View
|
|
@@ -117,7 +133,7 @@ const CustomMessageImage: React.FC<CustomMessageImageProps> = ({
|
|
|
117
133
|
style={{
|
|
118
134
|
width: dims.width,
|
|
119
135
|
height: dims.height,
|
|
120
|
-
borderRadius: 10,
|
|
136
|
+
borderRadius: tile ? 0 : 10,
|
|
121
137
|
backgroundColor: 'rgba(0, 0, 0, 0)',
|
|
122
138
|
}}
|
|
123
139
|
onLoadEnd={() => setLoading(false)}
|
|
@@ -17,6 +17,7 @@ import { getElementFont } from '../../helpers/getElementFont';
|
|
|
17
17
|
import { useTheme } from '../../hooks/useTheme';
|
|
18
18
|
import { useT } from '../../i18n/useT';
|
|
19
19
|
import { getMediaLibrary } from '../../helpers/mediaLibraryRuntime';
|
|
20
|
+
import { mediaFileCap, mediaSizeLimit } from '../../helpers/mediaLimits';
|
|
20
21
|
import * as ImagePicker from 'expo-image-picker';
|
|
21
22
|
import * as DocumentPicker from 'expo-document-picker';
|
|
22
23
|
import * as ImageManipulator from 'expo-image-manipulator';
|
|
@@ -44,14 +45,6 @@ import {
|
|
|
44
45
|
// receivers draw it without downloading and decoding the clip first.
|
|
45
46
|
const VOICE_PRESET = { ...RecordingPresets.HIGH_QUALITY, isMeteringEnabled: true };
|
|
46
47
|
|
|
47
|
-
// iOS photos default to HEIC; many web backends (incl. ours) 500 on
|
|
48
|
-
// HEIC uploads because they can't decode it. Convert to JPEG before
|
|
49
|
-
// upload — JPEG is universally supported. JPEGs pass through.
|
|
50
|
-
// Server rejects uploads over its body-size cap with HTTP 413 (Request
|
|
51
|
-
// Entity Too Large). Guard client-side so the user gets a clear message
|
|
52
|
-
// instead of a silent failed bubble. 50 MB matches the backend limit.
|
|
53
|
-
const MAX_MEDIA_BYTES = 50 * 1024 * 1024;
|
|
54
|
-
|
|
55
48
|
const formatBytes = (bytes: number): string => {
|
|
56
49
|
if (bytes >= 1024 * 1024) {
|
|
57
50
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
@@ -89,7 +82,11 @@ interface SendInputProps {
|
|
|
89
82
|
sendMessage: (message: string) => void;
|
|
90
83
|
isLoading: boolean;
|
|
91
84
|
editMessage?: string;
|
|
92
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Sends one message carrying `data` (a file or the picked list). The typed
|
|
87
|
+
* text, when there is any, goes with it as the caption.
|
|
88
|
+
*/
|
|
89
|
+
sendMedia: (data: any, type: string, caption?: { text: string }) => unknown;
|
|
93
90
|
config?: IConfig;
|
|
94
91
|
onFocus?: () => void;
|
|
95
92
|
onBlur?: () => void;
|
|
@@ -164,25 +161,64 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
164
161
|
// "Document" row or a dev-mode double-run of the sheet's close callback.
|
|
165
162
|
const pickingDocRef = useRef(false);
|
|
166
163
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Adds picked files to the tray. The tray is a list, not a slot: a second
|
|
166
|
+
* pick or a camera shot appends. Enforces the per-message file cap and the
|
|
167
|
+
* per-file size cap before anything reaches the network; the size cap is
|
|
168
|
+
* the upload limit (50 MB, or the sealed-file limit in an e2ee room),
|
|
169
|
+
* lowered by `config.attachments.maxSizeMB`. The backend rejects oversized
|
|
170
|
+
* bodies with HTTP 413, so catching them here gives a clear message.
|
|
171
|
+
*/
|
|
172
|
+
const handleFileSelect = (incoming: MediaFile[]) => {
|
|
173
|
+
if (incoming.length === 0) {return;}
|
|
174
|
+
const limit = mediaSizeLimit(config, maxMediaBytes);
|
|
175
|
+
const fitting = incoming.filter((file) => {
|
|
176
|
+
if (typeof file.size === 'number' && file.size > limit) {
|
|
177
|
+
Alert.alert(
|
|
178
|
+
t('attachment.tooLarge', {
|
|
179
|
+
name: file.name,
|
|
180
|
+
max: formatBytes(limit),
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
});
|
|
187
|
+
const cap = mediaFileCap(config);
|
|
188
|
+
const room = cap - filePreviewsRef.current.length;
|
|
189
|
+
if (fitting.length === 0) {return;}
|
|
190
|
+
if (room <= 0 || fitting.length > room) {
|
|
191
|
+
Alert.alert(t('attachment.limit', { count: cap }));
|
|
183
192
|
}
|
|
184
|
-
|
|
185
|
-
|
|
193
|
+
const accepted = fitting.slice(0, Math.max(0, room));
|
|
194
|
+
if (accepted.length === 0) {return;}
|
|
195
|
+
const next = [...filePreviewsRef.current, ...accepted];
|
|
196
|
+
filePreviewsRef.current = next;
|
|
197
|
+
setFilePreviews(next);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/** One picked image or video as the file the tray and the upload carry. */
|
|
201
|
+
const assetToFile = async (
|
|
202
|
+
asset: ImagePicker.ImagePickerAsset,
|
|
203
|
+
fallbackPrefix: string
|
|
204
|
+
): Promise<MediaFile> => {
|
|
205
|
+
if (asset.type === 'video') {
|
|
206
|
+
return {
|
|
207
|
+
uri: asset.uri,
|
|
208
|
+
type: asset.mimeType || 'video/mp4',
|
|
209
|
+
name: asset.fileName || asset.uri.split('/').pop() || `${fallbackPrefix}_${Date.now()}.mp4`,
|
|
210
|
+
size: (asset as any).fileSize,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// Videos pass through unchanged; only images get the HEIC→JPEG
|
|
214
|
+
// normalization (manipulator is image-only).
|
|
215
|
+
const normalized = await normalizeImageAsset(asset);
|
|
216
|
+
return {
|
|
217
|
+
uri: normalized.uri,
|
|
218
|
+
type: normalized.mime,
|
|
219
|
+
name: normalized.name,
|
|
220
|
+
size: (asset as any).fileSize,
|
|
221
|
+
};
|
|
186
222
|
};
|
|
187
223
|
|
|
188
224
|
// Both pickers use expo-image-picker / expo-document-picker. They
|
|
@@ -205,33 +241,14 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
205
241
|
}
|
|
206
242
|
|
|
207
243
|
try {
|
|
244
|
+
// Camera is a single shot per tap; the shot joins the tray.
|
|
208
245
|
const result = await ImagePicker.launchCameraAsync({
|
|
209
246
|
mediaTypes: ['images', 'videos'],
|
|
210
247
|
quality: 0.9,
|
|
211
248
|
videoQuality: ImagePicker.UIImagePickerControllerQualityType.Medium,
|
|
212
249
|
});
|
|
213
250
|
if (result.canceled || !result.assets?.[0]) {return;}
|
|
214
|
-
|
|
215
|
-
if (asset.type === 'video') {
|
|
216
|
-
handleFileSelect([
|
|
217
|
-
{
|
|
218
|
-
uri: asset.uri,
|
|
219
|
-
type: asset.mimeType || 'video/mp4',
|
|
220
|
-
name: asset.fileName || asset.uri.split('/').pop() || `camera_${Date.now()}.mp4`,
|
|
221
|
-
size: (asset as any).fileSize,
|
|
222
|
-
},
|
|
223
|
-
]);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
const normalized = await normalizeImageAsset(asset);
|
|
227
|
-
handleFileSelect([
|
|
228
|
-
{
|
|
229
|
-
uri: normalized.uri,
|
|
230
|
-
type: normalized.mime,
|
|
231
|
-
name: normalized.name,
|
|
232
|
-
size: (asset as any).fileSize,
|
|
233
|
-
},
|
|
234
|
-
]);
|
|
251
|
+
handleFileSelect([await assetToFile(result.assets[0], 'camera')]);
|
|
235
252
|
} catch (error) {
|
|
236
253
|
console.error('Camera error:', error);
|
|
237
254
|
}
|
|
@@ -244,38 +261,29 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
244
261
|
return;
|
|
245
262
|
}
|
|
246
263
|
|
|
264
|
+
// The library takes as many as the tray has room for (at least one).
|
|
265
|
+
const remaining = mediaFileCap(config) - filePreviewsRef.current.length;
|
|
266
|
+
if (remaining <= 0) {
|
|
267
|
+
Alert.alert(t('attachment.limit', { count: mediaFileCap(config) }));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
247
271
|
try {
|
|
248
272
|
const result = await ImagePicker.launchImageLibraryAsync({
|
|
249
273
|
mediaTypes: ['images', 'videos'],
|
|
250
274
|
allowsEditing: false,
|
|
251
275
|
quality: 0.9,
|
|
276
|
+
allowsMultipleSelection: true,
|
|
277
|
+
selectionLimit: remaining,
|
|
252
278
|
preferredAssetRepresentationMode:
|
|
253
279
|
ImagePicker.UIImagePickerPreferredAssetRepresentationMode.Compatible,
|
|
254
280
|
});
|
|
255
|
-
if (result.canceled || !result.assets?.
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (asset.type === 'video') {
|
|
260
|
-
handleFileSelect([
|
|
261
|
-
{
|
|
262
|
-
uri: asset.uri,
|
|
263
|
-
type: asset.mimeType || 'video/mp4',
|
|
264
|
-
name: asset.fileName || asset.uri.split('/').pop() || `gallery_${Date.now()}.mp4`,
|
|
265
|
-
size: (asset as any).fileSize,
|
|
266
|
-
},
|
|
267
|
-
]);
|
|
268
|
-
return;
|
|
281
|
+
if (result.canceled || !result.assets?.length) {return;}
|
|
282
|
+
const picked: MediaFile[] = [];
|
|
283
|
+
for (const asset of result.assets) {
|
|
284
|
+
picked.push(await assetToFile(asset, 'gallery'));
|
|
269
285
|
}
|
|
270
|
-
|
|
271
|
-
handleFileSelect([
|
|
272
|
-
{
|
|
273
|
-
uri: normalized.uri,
|
|
274
|
-
type: normalized.mime,
|
|
275
|
-
name: normalized.name,
|
|
276
|
-
size: (asset as any).fileSize,
|
|
277
|
-
},
|
|
278
|
-
]);
|
|
286
|
+
handleFileSelect(picked);
|
|
279
287
|
} catch (error) {
|
|
280
288
|
console.error('Gallery error:', error);
|
|
281
289
|
}
|
|
@@ -336,19 +344,18 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
336
344
|
try {
|
|
337
345
|
const result = await DocumentPicker.getDocumentAsync({
|
|
338
346
|
type: '*/*',
|
|
339
|
-
multiple:
|
|
347
|
+
multiple: true,
|
|
340
348
|
copyToCacheDirectory: true,
|
|
341
349
|
});
|
|
342
|
-
if (result.canceled || !result.assets?.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
{
|
|
350
|
+
if (result.canceled || !result.assets?.length) {return;}
|
|
351
|
+
handleFileSelect(
|
|
352
|
+
result.assets.map((asset) => ({
|
|
346
353
|
uri: asset.uri,
|
|
347
354
|
type: asset.mimeType || 'application/octet-stream',
|
|
348
355
|
name: asset.name || `file_${Date.now()}`,
|
|
349
356
|
size: asset.size ?? undefined,
|
|
350
|
-
}
|
|
351
|
-
|
|
357
|
+
}))
|
|
358
|
+
);
|
|
352
359
|
} catch (err) {
|
|
353
360
|
console.error('DocumentPicker error:', err);
|
|
354
361
|
} finally {
|
|
@@ -383,6 +390,11 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
383
390
|
const libraryPermissionCheckedRef = useRef(false);
|
|
384
391
|
|
|
385
392
|
const handleAttachPress = () => {
|
|
393
|
+
const cap = mediaFileCap(config);
|
|
394
|
+
if (filePreviewsRef.current.length >= cap) {
|
|
395
|
+
Alert.alert(t('attachment.limit', { count: cap }));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
386
398
|
if (libraryPermissionCheckedRef.current) {
|
|
387
399
|
setShowMediaMenu(true);
|
|
388
400
|
return;
|
|
@@ -594,17 +606,19 @@ const SendInput: React.FC<SendInputProps> = ({
|
|
|
594
606
|
setFilePreviews([]);
|
|
595
607
|
|
|
596
608
|
try {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (messageToSend) {
|
|
609
|
+
if (filesToSend.length > 0) {
|
|
610
|
+
// The files go as ONE message. The text typed with them is its
|
|
611
|
+
// caption, never a separate text message.
|
|
612
|
+
await sendMedia(
|
|
613
|
+
filesToSend,
|
|
614
|
+
filesToSend[0]!.type,
|
|
615
|
+
messageToSend ? { text: messageToSend } : undefined
|
|
616
|
+
);
|
|
617
|
+
} else if (messageToSend) {
|
|
606
618
|
sendMessage(messageToSend);
|
|
607
619
|
}
|
|
620
|
+
} catch (err) {
|
|
621
|
+
console.error(err);
|
|
608
622
|
} finally {
|
|
609
623
|
sendingRef.current = false;
|
|
610
624
|
}
|