@ethora/chat-component-rn 26.10.10 → 26.10.11
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 +16 -0
- package/README.md +125 -0
- package/lib/commonjs/booking/index.js +44 -0
- package/lib/commonjs/booking/index.js.map +1 -0
- package/lib/commonjs/booking/pendingScheduledCall.js +275 -0
- package/lib/commonjs/booking/pendingScheduledCall.js.map +1 -0
- package/lib/commonjs/booking/scheduleForm.js +91 -0
- package/lib/commonjs/booking/scheduleForm.js.map +1 -0
- package/lib/commonjs/booking/scheduledCall.js +141 -0
- package/lib/commonjs/booking/scheduledCall.js.map +1 -0
- package/lib/commonjs/booking/scheduledCallActions.js +128 -0
- package/lib/commonjs/booking/scheduledCallActions.js.map +1 -0
- package/lib/commonjs/booking/scheduledCallLink.js +143 -0
- package/lib/commonjs/booking/scheduledCallLink.js.map +1 -0
- package/lib/commonjs/booking/scheduledCallStore.js +64 -0
- package/lib/commonjs/booking/scheduledCallStore.js.map +1 -0
- package/lib/commonjs/booking/startRoomCall.js +84 -0
- package/lib/commonjs/booking/startRoomCall.js.map +1 -0
- package/lib/commonjs/calls/callBusy.js +18 -0
- package/lib/commonjs/calls/callBusy.js.map +1 -0
- package/lib/commonjs/calls/callDisplay.js +73 -0
- package/lib/commonjs/calls/callDisplay.js.map +1 -0
- package/lib/commonjs/calls/callMode.js +175 -0
- package/lib/commonjs/calls/callMode.js.map +1 -0
- package/lib/commonjs/calls/declineReport.js +20 -0
- package/lib/commonjs/calls/declineReport.js.map +1 -0
- package/lib/commonjs/calls/p2p/P2PCallController.js +724 -0
- package/lib/commonjs/calls/p2p/P2PCallController.js.map +1 -0
- package/lib/commonjs/calls/p2p/icePolicy.js +107 -0
- package/lib/commonjs/calls/p2p/icePolicy.js.map +1 -0
- package/lib/commonjs/calls/p2p/negotiator.js +202 -0
- package/lib/commonjs/calls/p2p/negotiator.js.map +1 -0
- package/lib/commonjs/calls/p2p/p2pRuntime.js +147 -0
- package/lib/commonjs/calls/p2p/p2pRuntime.js.map +1 -0
- package/lib/commonjs/calls/p2p/signalInbox.js +98 -0
- package/lib/commonjs/calls/p2p/signalInbox.js.map +1 -0
- package/lib/commonjs/calls/p2p/signalStanza.js +141 -0
- package/lib/commonjs/calls/p2p/signalStanza.js.map +1 -0
- package/lib/commonjs/calls/p2p/webrtcRuntime.js +53 -0
- package/lib/commonjs/calls/p2p/webrtcRuntime.js.map +1 -0
- package/lib/commonjs/calls/senderTrust.js +43 -0
- package/lib/commonjs/calls/senderTrust.js.map +1 -0
- package/lib/commonjs/calls/startCall.js +51 -0
- package/lib/commonjs/calls/startCall.js.map +1 -0
- package/lib/commonjs/components/Booking/BookingIcons.js +55 -0
- package/lib/commonjs/components/Booking/BookingIcons.js.map +1 -0
- package/lib/commonjs/components/Booking/ScheduleCallButton.js +64 -0
- package/lib/commonjs/components/Booking/ScheduleCallButton.js.map +1 -0
- package/lib/commonjs/components/Booking/ScheduleCallSheet.js +351 -0
- package/lib/commonjs/components/Booking/ScheduleCallSheet.js.map +1 -0
- package/lib/commonjs/components/Booking/ScheduledCallCard.js +203 -0
- package/lib/commonjs/components/Booking/ScheduledCallCard.js.map +1 -0
- package/lib/commonjs/components/Booking/ScheduledCallHost.js +23 -0
- package/lib/commonjs/components/Booking/ScheduledCallHost.js.map +1 -0
- package/lib/commonjs/components/Booking/ScheduledCallMessage.js +55 -0
- package/lib/commonjs/components/Booking/ScheduledCallMessage.js.map +1 -0
- package/lib/commonjs/components/Booking/ScheduledCallPreJoin.js +235 -0
- package/lib/commonjs/components/Booking/ScheduledCallPreJoin.js.map +1 -0
- package/lib/commonjs/components/Booking/bookingShared.js +305 -0
- package/lib/commonjs/components/Booking/bookingShared.js.map +1 -0
- package/lib/commonjs/components/MainComponents/ChatHeader.js +2 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/LanguageSelectorButton.js +5 -2
- package/lib/commonjs/components/MainComponents/LanguageSelectorButton.js.map +1 -1
- package/lib/commonjs/components/MainComponents/LanguageSelectorModal.js +6 -3
- package/lib/commonjs/components/MainComponents/LanguageSelectorModal.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MessageContainer.js +13 -0
- package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js +10 -0
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js +19 -15
- package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +15 -8
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserSettingsModal/LanguageCard.js +1 -1
- package/lib/commonjs/components/Modals/UserSettingsModal/LanguageCard.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js +3 -2
- package/lib/commonjs/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/CallButtons.js +19 -11
- package/lib/commonjs/components/VideoCalls/CallButtons.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/CallControlBar.js +119 -0
- package/lib/commonjs/components/VideoCalls/CallControlBar.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/CallVideo.js +49 -0
- package/lib/commonjs/components/VideoCalls/CallVideo.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/GroupCallView.js +281 -0
- package/lib/commonjs/components/VideoCalls/GroupCallView.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/P2PCallRunner.js +151 -0
- package/lib/commonjs/components/VideoCalls/P2PCallRunner.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +38 -14
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js +57 -128
- package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callActions.js +3 -0
- package/lib/commonjs/components/VideoCalls/callActions.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callElapsed.js +38 -0
- package/lib/commonjs/components/VideoCalls/callElapsed.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/callLive.js +15 -0
- package/lib/commonjs/components/VideoCalls/callLive.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/callSessionController.js +81 -24
- package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callSessionTypes.js +41 -0
- package/lib/commonjs/components/VideoCalls/callSessionTypes.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/groupLayout.js +43 -0
- package/lib/commonjs/components/VideoCalls/groupLayout.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/p2pCallSession.js +272 -0
- package/lib/commonjs/components/VideoCalls/p2pCallSession.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/useCallKeep.js +7 -2
- package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js +2 -1
- package/lib/commonjs/components/VideoCalls/useOngoingCallNotification.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +2 -1
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/callPush.js +3 -1
- package/lib/commonjs/helpers/callPush.js.map +1 -1
- package/lib/commonjs/helpers/incomingCallFromPush.js +63 -7
- package/lib/commonjs/helpers/incomingCallFromPush.js.map +1 -1
- package/lib/commonjs/helpers/pushPayload.js +5 -0
- package/lib/commonjs/helpers/pushPayload.js.map +1 -1
- package/lib/commonjs/helpers/reportRoom.js +76 -0
- package/lib/commonjs/helpers/reportRoom.js.map +1 -0
- package/lib/commonjs/i18n/strings.js +444 -0
- package/lib/commonjs/i18n/strings.js.map +1 -1
- package/lib/commonjs/main.js +93 -0
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/api-requests/callState.api.js +75 -0
- package/lib/commonjs/networking/api-requests/callState.api.js.map +1 -0
- package/lib/commonjs/networking/api-requests/push.api.js +29 -6
- package/lib/commonjs/networking/api-requests/push.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/rooms.api.js +8 -2
- package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/scheduledCalls.api.js +141 -0
- package/lib/commonjs/networking/api-requests/scheduledCalls.api.js.map +1 -0
- package/lib/commonjs/networking/callTokenStanza.js +210 -50
- package/lib/commonjs/networking/callTokenStanza.js.map +1 -1
- package/lib/commonjs/roomStore/callSlice.js +59 -4
- package/lib/commonjs/roomStore/callSlice.js.map +1 -1
- package/lib/commonjs/services/appBadge.js +80 -0
- package/lib/commonjs/services/appBadge.js.map +1 -0
- package/lib/commonjs/services/languageSettings.js +47 -15
- package/lib/commonjs/services/languageSettings.js.map +1 -1
- package/lib/commonjs/services/pushRegistration.js +21 -5
- package/lib/commonjs/services/pushRegistration.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/booking/index.js +5 -0
- package/lib/module/booking/index.js.map +1 -0
- package/lib/module/booking/pendingScheduledCall.js +261 -0
- package/lib/module/booking/pendingScheduledCall.js.map +1 -0
- package/lib/module/booking/scheduleForm.js +79 -0
- package/lib/module/booking/scheduleForm.js.map +1 -0
- package/lib/module/booking/scheduledCall.js +129 -0
- package/lib/module/booking/scheduledCall.js.map +1 -0
- package/lib/module/booking/scheduledCallActions.js +118 -0
- package/lib/module/booking/scheduledCallActions.js.map +1 -0
- package/lib/module/booking/scheduledCallLink.js +136 -0
- package/lib/module/booking/scheduledCallLink.js.map +1 -0
- package/lib/module/booking/scheduledCallStore.js +60 -0
- package/lib/module/booking/scheduledCallStore.js.map +1 -0
- package/lib/module/booking/startRoomCall.js +80 -0
- package/lib/module/booking/startRoomCall.js.map +1 -0
- package/lib/module/calls/callBusy.js +13 -0
- package/lib/module/calls/callBusy.js.map +1 -0
- package/lib/module/calls/callDisplay.js +65 -0
- package/lib/module/calls/callDisplay.js.map +1 -0
- package/lib/module/calls/callMode.js +168 -0
- package/lib/module/calls/callMode.js.map +1 -0
- package/lib/module/calls/declineReport.js +16 -0
- package/lib/module/calls/declineReport.js.map +1 -0
- package/lib/module/calls/p2p/P2PCallController.js +718 -0
- package/lib/module/calls/p2p/P2PCallController.js.map +1 -0
- package/lib/module/calls/p2p/icePolicy.js +102 -0
- package/lib/module/calls/p2p/icePolicy.js.map +1 -0
- package/lib/module/calls/p2p/negotiator.js +197 -0
- package/lib/module/calls/p2p/negotiator.js.map +1 -0
- package/lib/module/calls/p2p/p2pRuntime.js +137 -0
- package/lib/module/calls/p2p/p2pRuntime.js.map +1 -0
- package/lib/module/calls/p2p/signalInbox.js +93 -0
- package/lib/module/calls/p2p/signalInbox.js.map +1 -0
- package/lib/module/calls/p2p/signalStanza.js +135 -0
- package/lib/module/calls/p2p/signalStanza.js.map +1 -0
- package/lib/module/calls/p2p/webrtcRuntime.js +46 -0
- package/lib/module/calls/p2p/webrtcRuntime.js.map +1 -0
- package/lib/module/calls/senderTrust.js +38 -0
- package/lib/module/calls/senderTrust.js.map +1 -0
- package/lib/module/calls/startCall.js +47 -0
- package/lib/module/calls/startCall.js.map +1 -0
- package/lib/module/components/Booking/BookingIcons.js +48 -0
- package/lib/module/components/Booking/BookingIcons.js.map +1 -0
- package/lib/module/components/Booking/ScheduleCallButton.js +59 -0
- package/lib/module/components/Booking/ScheduleCallButton.js.map +1 -0
- package/lib/module/components/Booking/ScheduleCallSheet.js +345 -0
- package/lib/module/components/Booking/ScheduleCallSheet.js.map +1 -0
- package/lib/module/components/Booking/ScheduledCallCard.js +197 -0
- package/lib/module/components/Booking/ScheduledCallCard.js.map +1 -0
- package/lib/module/components/Booking/ScheduledCallHost.js +18 -0
- package/lib/module/components/Booking/ScheduledCallHost.js.map +1 -0
- package/lib/module/components/Booking/ScheduledCallMessage.js +48 -0
- package/lib/module/components/Booking/ScheduledCallMessage.js.map +1 -0
- package/lib/module/components/Booking/ScheduledCallPreJoin.js +229 -0
- package/lib/module/components/Booking/ScheduledCallPreJoin.js.map +1 -0
- package/lib/module/components/Booking/bookingShared.js +291 -0
- package/lib/module/components/Booking/bookingShared.js.map +1 -0
- package/lib/module/components/MainComponents/ChatHeader.js +2 -1
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/LanguageSelectorButton.js +5 -2
- package/lib/module/components/MainComponents/LanguageSelectorButton.js.map +1 -1
- package/lib/module/components/MainComponents/LanguageSelectorModal.js +7 -4
- package/lib/module/components/MainComponents/LanguageSelectorModal.js.map +1 -1
- package/lib/module/components/MainComponents/MessageContainer.js +13 -0
- package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
- package/lib/module/components/MainComponents/ReduxWrapper.js +10 -0
- package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js +21 -15
- package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +17 -10
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/module/components/Modals/UserSettingsModal/LanguageCard.js +1 -1
- package/lib/module/components/Modals/UserSettingsModal/LanguageCard.js.map +1 -1
- package/lib/module/components/VideoCalls/ActiveCallBar.js +3 -2
- package/lib/module/components/VideoCalls/ActiveCallBar.js.map +1 -1
- package/lib/module/components/VideoCalls/CallButtons.js +19 -11
- package/lib/module/components/VideoCalls/CallButtons.js.map +1 -1
- package/lib/module/components/VideoCalls/CallControlBar.js +113 -0
- package/lib/module/components/VideoCalls/CallControlBar.js.map +1 -0
- package/lib/module/components/VideoCalls/CallVideo.js +43 -0
- package/lib/module/components/VideoCalls/CallVideo.js.map +1 -0
- package/lib/module/components/VideoCalls/GroupCallView.js +275 -0
- package/lib/module/components/VideoCalls/GroupCallView.js.map +1 -0
- package/lib/module/components/VideoCalls/P2PCallRunner.js +147 -0
- package/lib/module/components/VideoCalls/P2PCallRunner.js.map +1 -0
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +38 -14
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallSession.js +46 -127
- package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -1
- package/lib/module/components/VideoCalls/callActions.js +3 -0
- package/lib/module/components/VideoCalls/callActions.js.map +1 -1
- package/lib/module/components/VideoCalls/callElapsed.js +33 -0
- package/lib/module/components/VideoCalls/callElapsed.js.map +1 -0
- package/lib/module/components/VideoCalls/callLive.js +10 -0
- package/lib/module/components/VideoCalls/callLive.js.map +1 -0
- package/lib/module/components/VideoCalls/callSessionController.js +80 -24
- package/lib/module/components/VideoCalls/callSessionController.js.map +1 -1
- package/lib/module/components/VideoCalls/callSessionTypes.js +37 -0
- package/lib/module/components/VideoCalls/callSessionTypes.js.map +1 -0
- package/lib/module/components/VideoCalls/groupLayout.js +36 -0
- package/lib/module/components/VideoCalls/groupLayout.js.map +1 -0
- package/lib/module/components/VideoCalls/p2pCallSession.js +256 -0
- package/lib/module/components/VideoCalls/p2pCallSession.js.map +1 -0
- package/lib/module/components/VideoCalls/useCallKeep.js +8 -2
- package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js +2 -1
- package/lib/module/components/VideoCalls/useOngoingCallNotification.js.map +1 -1
- package/lib/module/context/xmppProvider.js +2 -1
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/callPush.js +3 -1
- package/lib/module/helpers/callPush.js.map +1 -1
- package/lib/module/helpers/incomingCallFromPush.js +63 -7
- package/lib/module/helpers/incomingCallFromPush.js.map +1 -1
- package/lib/module/helpers/pushPayload.js +5 -0
- package/lib/module/helpers/pushPayload.js.map +1 -1
- package/lib/module/helpers/reportRoom.js +70 -0
- package/lib/module/helpers/reportRoom.js.map +1 -0
- package/lib/module/i18n/strings.js +444 -0
- package/lib/module/i18n/strings.js.map +1 -1
- package/lib/module/main.js +5 -0
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/api-requests/callState.api.js +66 -0
- package/lib/module/networking/api-requests/callState.api.js.map +1 -0
- package/lib/module/networking/api-requests/push.api.js +29 -6
- package/lib/module/networking/api-requests/push.api.js.map +1 -1
- package/lib/module/networking/api-requests/rooms.api.js +8 -2
- package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/module/networking/api-requests/scheduledCalls.api.js +130 -0
- package/lib/module/networking/api-requests/scheduledCalls.api.js.map +1 -0
- package/lib/module/networking/callTokenStanza.js +210 -50
- package/lib/module/networking/callTokenStanza.js.map +1 -1
- package/lib/module/roomStore/callSlice.js +57 -3
- package/lib/module/roomStore/callSlice.js.map +1 -1
- package/lib/module/services/appBadge.js +74 -0
- package/lib/module/services/appBadge.js.map +1 -0
- package/lib/module/services/languageSettings.js +44 -13
- package/lib/module/services/languageSettings.js.map +1 -1
- package/lib/module/services/pushRegistration.js +21 -5
- package/lib/module/services/pushRegistration.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/booking/index.d.ts +3 -0
- package/lib/typescript/booking/index.d.ts.map +1 -0
- package/lib/typescript/booking/pendingScheduledCall.d.ts +80 -0
- package/lib/typescript/booking/pendingScheduledCall.d.ts.map +1 -0
- package/lib/typescript/booking/scheduleForm.d.ts +28 -0
- package/lib/typescript/booking/scheduleForm.d.ts.map +1 -0
- package/lib/typescript/booking/scheduledCall.d.ts +52 -0
- package/lib/typescript/booking/scheduledCall.d.ts.map +1 -0
- package/lib/typescript/booking/scheduledCallActions.d.ts +22 -0
- package/lib/typescript/booking/scheduledCallActions.d.ts.map +1 -0
- package/lib/typescript/booking/scheduledCallLink.d.ts +37 -0
- package/lib/typescript/booking/scheduledCallLink.d.ts.map +1 -0
- package/lib/typescript/booking/scheduledCallStore.d.ts +47 -0
- package/lib/typescript/booking/scheduledCallStore.d.ts.map +1 -0
- package/lib/typescript/booking/startRoomCall.d.ts +12 -0
- package/lib/typescript/booking/startRoomCall.d.ts.map +1 -0
- package/lib/typescript/calls/callBusy.d.ts +9 -0
- package/lib/typescript/calls/callBusy.d.ts.map +1 -0
- package/lib/typescript/calls/callDisplay.d.ts +23 -0
- package/lib/typescript/calls/callDisplay.d.ts.map +1 -0
- package/lib/typescript/calls/callMode.d.ts +106 -0
- package/lib/typescript/calls/callMode.d.ts.map +1 -0
- package/lib/typescript/calls/declineReport.d.ts +11 -0
- package/lib/typescript/calls/declineReport.d.ts.map +1 -0
- package/lib/typescript/calls/p2p/P2PCallController.d.ts +150 -0
- package/lib/typescript/calls/p2p/P2PCallController.d.ts.map +1 -0
- package/lib/typescript/calls/p2p/icePolicy.d.ts +37 -0
- package/lib/typescript/calls/p2p/icePolicy.d.ts.map +1 -0
- package/lib/typescript/calls/p2p/negotiator.d.ts +57 -0
- package/lib/typescript/calls/p2p/negotiator.d.ts.map +1 -0
- package/lib/typescript/calls/p2p/p2pRuntime.d.ts +41 -0
- package/lib/typescript/calls/p2p/p2pRuntime.d.ts.map +1 -0
- package/lib/typescript/calls/p2p/signalInbox.d.ts +35 -0
- package/lib/typescript/calls/p2p/signalInbox.d.ts.map +1 -0
- package/lib/typescript/calls/p2p/signalStanza.d.ts +52 -0
- package/lib/typescript/calls/p2p/signalStanza.d.ts.map +1 -0
- package/lib/typescript/calls/p2p/webrtcRuntime.d.ts +32 -0
- package/lib/typescript/calls/p2p/webrtcRuntime.d.ts.map +1 -0
- package/lib/typescript/calls/senderTrust.d.ts +8 -0
- package/lib/typescript/calls/senderTrust.d.ts.map +1 -0
- package/lib/typescript/calls/startCall.d.ts +17 -0
- package/lib/typescript/calls/startCall.d.ts.map +1 -0
- package/lib/typescript/components/Booking/BookingIcons.d.ts +9 -0
- package/lib/typescript/components/Booking/BookingIcons.d.ts.map +1 -0
- package/lib/typescript/components/Booking/ScheduleCallButton.d.ts +11 -0
- package/lib/typescript/components/Booking/ScheduleCallButton.d.ts.map +1 -0
- package/lib/typescript/components/Booking/ScheduleCallSheet.d.ts +4 -0
- package/lib/typescript/components/Booking/ScheduleCallSheet.d.ts.map +1 -0
- package/lib/typescript/components/Booking/ScheduledCallCard.d.ts +14 -0
- package/lib/typescript/components/Booking/ScheduledCallCard.d.ts.map +1 -0
- package/lib/typescript/components/Booking/ScheduledCallHost.d.ts +10 -0
- package/lib/typescript/components/Booking/ScheduledCallHost.d.ts.map +1 -0
- package/lib/typescript/components/Booking/ScheduledCallMessage.d.ts +20 -0
- package/lib/typescript/components/Booking/ScheduledCallMessage.d.ts.map +1 -0
- package/lib/typescript/components/Booking/ScheduledCallPreJoin.d.ts +8 -0
- package/lib/typescript/components/Booking/ScheduledCallPreJoin.d.ts.map +1 -0
- package/lib/typescript/components/Booking/bookingShared.d.ts +199 -0
- package/lib/typescript/components/Booking/bookingShared.d.ts.map +1 -0
- package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/LanguageSelectorButton.d.ts +3 -1
- package/lib/typescript/components/MainComponents/LanguageSelectorButton.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/LanguageSelectorModal.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserSettingsModal/LanguageCard.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/ActiveCallBar.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/CallButtons.d.ts +2 -1
- package/lib/typescript/components/VideoCalls/CallButtons.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/CallControlBar.d.ts +17 -0
- package/lib/typescript/components/VideoCalls/CallControlBar.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/CallVideo.d.ts +23 -0
- package/lib/typescript/components/VideoCalls/CallVideo.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/GroupCallView.d.ts +22 -0
- package/lib/typescript/components/VideoCalls/GroupCallView.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/P2PCallRunner.d.ts +2 -0
- package/lib/typescript/components/VideoCalls/P2PCallRunner.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts +10 -12
- package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callActions.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callElapsed.d.ts +8 -0
- package/lib/typescript/components/VideoCalls/callElapsed.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/callLive.d.ts +9 -0
- package/lib/typescript/components/VideoCalls/callLive.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts +10 -31
- package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callSessionTypes.d.ts +60 -0
- package/lib/typescript/components/VideoCalls/callSessionTypes.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/groupLayout.d.ts +23 -0
- package/lib/typescript/components/VideoCalls/groupLayout.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/p2pCallSession.d.ts +31 -0
- package/lib/typescript/components/VideoCalls/p2pCallSession.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useOngoingCallNotification.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/callPush.d.ts +11 -1
- package/lib/typescript/helpers/callPush.d.ts.map +1 -1
- package/lib/typescript/helpers/incomingCallFromPush.d.ts.map +1 -1
- package/lib/typescript/helpers/pushPayload.d.ts +1 -1
- package/lib/typescript/helpers/pushPayload.d.ts.map +1 -1
- package/lib/typescript/helpers/reportRoom.d.ts +19 -0
- package/lib/typescript/helpers/reportRoom.d.ts.map +1 -0
- package/lib/typescript/i18n/strings.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +6 -1
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/callState.api.d.ts +25 -0
- package/lib/typescript/networking/api-requests/callState.api.d.ts.map +1 -0
- package/lib/typescript/networking/api-requests/push.api.d.ts +9 -2
- package/lib/typescript/networking/api-requests/push.api.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/rooms.api.d.ts +2 -1
- package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/scheduledCalls.api.d.ts +86 -0
- package/lib/typescript/networking/api-requests/scheduledCalls.api.d.ts.map +1 -0
- package/lib/typescript/networking/callTokenStanza.d.ts.map +1 -1
- package/lib/typescript/roomStore/callSlice.d.ts +23 -1
- package/lib/typescript/roomStore/callSlice.d.ts.map +1 -1
- package/lib/typescript/services/appBadge.d.ts +3 -0
- package/lib/typescript/services/appBadge.d.ts.map +1 -0
- package/lib/typescript/services/languageSettings.d.ts +7 -0
- package/lib/typescript/services/languageSettings.d.ts.map +1 -1
- package/lib/typescript/services/pushRegistration.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +67 -4
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/booking/index.ts +10 -0
- package/src/booking/pendingScheduledCall.ts +272 -0
- package/src/booking/scheduleForm.ts +84 -0
- package/src/booking/scheduledCall.ts +163 -0
- package/src/booking/scheduledCallActions.ts +141 -0
- package/src/booking/scheduledCallLink.ts +145 -0
- package/src/booking/scheduledCallStore.ts +69 -0
- package/src/booking/startRoomCall.ts +103 -0
- package/src/calls/callBusy.ts +10 -0
- package/src/calls/callDisplay.ts +74 -0
- package/src/calls/callMode.ts +286 -0
- package/src/calls/declineReport.ts +16 -0
- package/src/calls/p2p/P2PCallController.ts +813 -0
- package/src/calls/p2p/icePolicy.ts +99 -0
- package/src/calls/p2p/negotiator.ts +186 -0
- package/src/calls/p2p/p2pRuntime.ts +164 -0
- package/src/calls/p2p/signalInbox.ts +117 -0
- package/src/calls/p2p/signalStanza.ts +205 -0
- package/src/calls/p2p/webrtcRuntime.ts +60 -0
- package/src/calls/senderTrust.ts +40 -0
- package/src/calls/startCall.ts +56 -0
- package/src/components/Booking/BookingIcons.tsx +44 -0
- package/src/components/Booking/ScheduleCallButton.tsx +66 -0
- package/src/components/Booking/ScheduleCallSheet.tsx +380 -0
- package/src/components/Booking/ScheduledCallCard.tsx +216 -0
- package/src/components/Booking/ScheduledCallHost.tsx +18 -0
- package/src/components/Booking/ScheduledCallMessage.tsx +62 -0
- package/src/components/Booking/ScheduledCallPreJoin.tsx +230 -0
- package/src/components/Booking/bookingShared.tsx +295 -0
- package/src/components/MainComponents/ChatHeader.tsx +2 -0
- package/src/components/MainComponents/LanguageSelectorButton.tsx +9 -2
- package/src/components/MainComponents/LanguageSelectorModal.tsx +10 -6
- package/src/components/MainComponents/MessageContainer.tsx +15 -0
- package/src/components/MainComponents/ReduxWrapper.tsx +10 -0
- package/src/components/Modals/ChatProfileModal/ReportChatModal.tsx +22 -22
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +28 -9
- package/src/components/Modals/UserSettingsModal/LanguageCard.tsx +9 -7
- package/src/components/VideoCalls/ActiveCallBar.tsx +6 -5
- package/src/components/VideoCalls/CallButtons.tsx +23 -12
- package/src/components/VideoCalls/CallControlBar.tsx +131 -0
- package/src/components/VideoCalls/CallVideo.tsx +59 -0
- package/src/components/VideoCalls/GroupCallView.tsx +324 -0
- package/src/components/VideoCalls/P2PCallRunner.tsx +192 -0
- package/src/components/VideoCalls/VideoCallOverlay.tsx +62 -17
- package/src/components/VideoCalls/VideoCallSession.tsx +55 -133
- package/src/components/VideoCalls/callActions.ts +3 -0
- package/src/components/VideoCalls/callElapsed.ts +27 -0
- package/src/components/VideoCalls/callLive.ts +13 -0
- package/src/components/VideoCalls/callSessionController.ts +130 -55
- package/src/components/VideoCalls/callSessionTypes.ts +80 -0
- package/src/components/VideoCalls/groupLayout.ts +48 -0
- package/src/components/VideoCalls/p2pCallSession.ts +291 -0
- package/src/components/VideoCalls/useCallKeep.ts +9 -2
- package/src/components/VideoCalls/useOngoingCallNotification.ts +2 -2
- package/src/context/xmppProvider.tsx +3 -0
- package/src/helpers/callPush.ts +13 -1
- package/src/helpers/incomingCallFromPush.ts +65 -7
- package/src/helpers/pushPayload.ts +9 -1
- package/src/helpers/reportRoom.ts +93 -0
- package/src/i18n/strings.ts +444 -0
- package/src/main.ts +33 -0
- package/src/networking/api-requests/callState.api.ts +83 -0
- package/src/networking/api-requests/push.api.ts +28 -3
- package/src/networking/api-requests/rooms.api.ts +12 -3
- package/src/networking/api-requests/scheduledCalls.api.ts +232 -0
- package/src/networking/callTokenStanza.ts +247 -50
- package/src/roomStore/callSlice.ts +86 -2
- package/src/services/appBadge.ts +85 -0
- package/src/services/languageSettings.ts +43 -8
- package/src/services/pushRegistration.ts +34 -3
- package/src/types/types.ts +70 -4
- package/src/version.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { FC,
|
|
1
|
+
import React, { FC, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ActivityIndicator,
|
|
4
4
|
Animated,
|
|
@@ -12,27 +12,18 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
12
12
|
import { VideoCallIcons } from '../../types/types';
|
|
13
13
|
import { ProfileImagePlaceholder } from '../MainComponents/ProfileImagePlaceholder';
|
|
14
14
|
import {
|
|
15
|
-
CameraOffIcon,
|
|
16
|
-
CameraOnIcon,
|
|
17
15
|
FillVideoIcon,
|
|
18
16
|
FitVideoIcon,
|
|
19
|
-
HangUpIcon,
|
|
20
17
|
MicOffIcon,
|
|
21
|
-
MicOnIcon,
|
|
22
18
|
MinimizeIcon,
|
|
23
|
-
SpeakerIcon,
|
|
24
|
-
SpeakerOffIcon,
|
|
25
|
-
SwitchCameraIcon,
|
|
26
19
|
} from './CallIcons';
|
|
27
20
|
import { loadLiveKit } from './livekitRuntime';
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
useCallSession,
|
|
35
|
-
} from './callSessionController';
|
|
21
|
+
import { loadWebRTC } from '../../calls/p2p/webrtcRuntime';
|
|
22
|
+
import { dismissCallDeviceError, useCallSession } from './callSessionController';
|
|
23
|
+
import { CallVideo } from './CallVideo';
|
|
24
|
+
import { CallControlBar } from './CallControlBar';
|
|
25
|
+
import { GroupCallView } from './GroupCallView';
|
|
26
|
+
import { formatCallDuration, useSharedCallElapsed } from './callElapsed';
|
|
36
27
|
import { usePipTile } from './usePipTile';
|
|
37
28
|
import { useT } from '../../i18n/useT';
|
|
38
29
|
|
|
@@ -40,11 +31,15 @@ const DEFAULT_PRIMARY = '#0052CD';
|
|
|
40
31
|
const SURFACE_DARK = '#0B1220';
|
|
41
32
|
const TEXT_ON_DARK = '#FFFFFF';
|
|
42
33
|
const TEXT_MUTED = '#9AA4B2';
|
|
43
|
-
const DANGER = '#E53935';
|
|
44
34
|
const CONTROL_IDLE = 'rgba(255, 255, 255, 0.16)';
|
|
45
35
|
|
|
36
|
+
// Re-exported for the header strip, which shares the timer with this screen.
|
|
37
|
+
export { formatCallDuration, useSharedCallElapsed };
|
|
38
|
+
|
|
46
39
|
export interface VideoCallSessionProps {
|
|
47
40
|
kind?: 'audio' | 'video';
|
|
41
|
+
/** Group room call: everyone in a grid, instead of the 1:1 stage. */
|
|
42
|
+
isGroup?: boolean;
|
|
48
43
|
/** Display name of the peer, shown until LiveKit reports theirs. */
|
|
49
44
|
peerName?: string | null;
|
|
50
45
|
primaryColor?: string;
|
|
@@ -55,42 +50,18 @@ export interface VideoCallSessionProps {
|
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
|
|
62
|
-
export const useSharedCallElapsed = (anchorAt: number | null): number | null => {
|
|
63
|
-
const [, tick] = useState(0);
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
if (anchorAt === null) {return;}
|
|
66
|
-
const id = setInterval(() => tick((n) => n + 1), 1000);
|
|
67
|
-
return () => clearInterval(id);
|
|
68
|
-
}, [anchorAt]);
|
|
69
|
-
if (anchorAt === null) {return null;}
|
|
70
|
-
return Math.max(0, Math.floor((Date.now() - anchorAt) / 1000));
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const formatCallDuration = (totalSeconds: number): string => {
|
|
74
|
-
const safe = Math.max(0, Math.floor(totalSeconds));
|
|
75
|
-
const hours = Math.floor(safe / 3600);
|
|
76
|
-
const minutes = Math.floor((safe % 3600) / 60);
|
|
77
|
-
const seconds = safe % 60;
|
|
78
|
-
const mm = String(minutes).padStart(2, '0');
|
|
79
|
-
const ss = String(seconds).padStart(2, '0');
|
|
80
|
-
return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* The call screen, React Native edition. Pure view over the LiveKit session
|
|
85
|
-
* owned by callSessionController.ts: it can unmount and remount (minimize,
|
|
86
|
-
* leaving the chat screen) without touching the connection.
|
|
53
|
+
* The call screen, React Native edition. Pure view over the media session
|
|
54
|
+
* of the call (the LiveKit room in callSessionController.ts, or the peer to
|
|
55
|
+
* peer connection in p2pCallSession.ts): it can unmount and remount
|
|
56
|
+
* (minimize, leaving the chat screen) without touching the connection.
|
|
87
57
|
*
|
|
88
58
|
* The web SDK builds this on `@livekit/components-react` hooks, which
|
|
89
|
-
* assume a DOM. Only the video
|
|
90
|
-
* (`<VideoTrack
|
|
59
|
+
* assume a DOM. Only the video surfaces come from the native packages
|
|
60
|
+
* (`<CallVideo />`: LiveKit's `<VideoTrack />` or the fork's `<RTCView />`).
|
|
91
61
|
*/
|
|
92
62
|
export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
93
63
|
kind = 'video',
|
|
64
|
+
isGroup = false,
|
|
94
65
|
peerName,
|
|
95
66
|
primaryColor = DEFAULT_PRIMARY,
|
|
96
67
|
icons,
|
|
@@ -99,7 +70,8 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
99
70
|
}) => {
|
|
100
71
|
const t = useT();
|
|
101
72
|
const insets = useSafeAreaInsets();
|
|
102
|
-
|
|
73
|
+
// A p2p call needs the WebRTC package only, a LiveKit call the LiveKit one.
|
|
74
|
+
const hasMedia = !!loadLiveKit() || !!loadWebRTC();
|
|
103
75
|
const session = useCallSession();
|
|
104
76
|
const pipTile = usePipTile({ topInset: insets.top });
|
|
105
77
|
const elapsed = useSharedCallElapsed(session.anchorAt);
|
|
@@ -110,7 +82,7 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
110
82
|
// minimize stretches it to fill (the old behaviour) and back.
|
|
111
83
|
const [fillRemote, setFillRemote] = useState(false);
|
|
112
84
|
|
|
113
|
-
if (!
|
|
85
|
+
if (!hasMedia) {
|
|
114
86
|
return (
|
|
115
87
|
<View style={[styles.canvas, styles.centered]}>
|
|
116
88
|
<Text style={styles.errorText}>{t('call.error.sdkMissing')}</Text>
|
|
@@ -118,7 +90,20 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
118
90
|
);
|
|
119
91
|
}
|
|
120
92
|
|
|
121
|
-
|
|
93
|
+
if (isGroup) {
|
|
94
|
+
return (
|
|
95
|
+
<GroupCallView
|
|
96
|
+
session={session}
|
|
97
|
+
kind={kind}
|
|
98
|
+
title={peerName}
|
|
99
|
+
primaryColor={primaryColor}
|
|
100
|
+
icons={icons}
|
|
101
|
+
onToggleMinimize={onToggleMinimize}
|
|
102
|
+
onHangup={onHangup}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
122
107
|
const {
|
|
123
108
|
connected,
|
|
124
109
|
remoteJoined,
|
|
@@ -126,11 +111,11 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
126
111
|
localVideo,
|
|
127
112
|
micOn,
|
|
128
113
|
cameraOn,
|
|
129
|
-
speakerOn,
|
|
130
114
|
deviceError,
|
|
131
115
|
remoteMicMuted,
|
|
132
116
|
facingMode,
|
|
133
117
|
localVideoEpoch,
|
|
118
|
+
reconnecting,
|
|
134
119
|
} = session;
|
|
135
120
|
// The call slice's name (the chat title) first, as web does: LiveKit's
|
|
136
121
|
// participant name is often just the opaque xmpp identity.
|
|
@@ -150,11 +135,16 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
150
135
|
const selfMirror = facingMode === 'user';
|
|
151
136
|
// The local view remounts when the camera is re-acquired (switching
|
|
152
137
|
// front/back), so it never keeps rendering the stopped stream.
|
|
138
|
+
// A p2p stream handle also carries its track ids (`key`), so a track that
|
|
139
|
+
// arrives or is replaced remounts the view.
|
|
153
140
|
const videoKey = (ref: any) =>
|
|
154
|
-
ref === localVideo ? `local-${localVideoEpoch}` : 'remote'
|
|
141
|
+
`${ref === localVideo ? `local-${localVideoEpoch}` : 'remote'}${
|
|
142
|
+
ref?.key ? `:${ref.key}` : ''
|
|
143
|
+
}`;
|
|
155
144
|
|
|
156
|
-
const statusText =
|
|
157
|
-
|
|
145
|
+
const statusText = reconnecting
|
|
146
|
+
? t('call.status.reconnecting')
|
|
147
|
+
: elapsed !== null
|
|
158
148
|
? formatCallDuration(elapsed)
|
|
159
149
|
: !connected
|
|
160
150
|
? t('call.status.connecting')
|
|
@@ -169,9 +159,9 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
169
159
|
{/* Stage */}
|
|
170
160
|
<View style={styles.stage} onLayout={pipTile.onStageLayout}>
|
|
171
161
|
{mainVideo ? (
|
|
172
|
-
<
|
|
162
|
+
<CallVideo
|
|
173
163
|
key={videoKey(mainVideo)}
|
|
174
|
-
|
|
164
|
+
source={mainVideo}
|
|
175
165
|
style={styles.remoteVideo}
|
|
176
166
|
objectFit={mainFit}
|
|
177
167
|
mirror={isSwapped && selfMirror}
|
|
@@ -232,9 +222,9 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
232
222
|
accessibilityRole="button"
|
|
233
223
|
accessibilityLabel={t('call.action.swapVideo')}
|
|
234
224
|
>
|
|
235
|
-
<
|
|
225
|
+
<CallVideo
|
|
236
226
|
key={videoKey(pipVideo)}
|
|
237
|
-
|
|
227
|
+
source={pipVideo}
|
|
238
228
|
style={styles.pipVideo}
|
|
239
229
|
objectFit="cover"
|
|
240
230
|
mirror={!isSwapped && selfMirror}
|
|
@@ -286,62 +276,12 @@ export const VideoCallSession: FC<VideoCallSessionProps> = ({
|
|
|
286
276
|
</View>
|
|
287
277
|
|
|
288
278
|
{/* Control bar */}
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
>
|
|
296
|
-
{micOn
|
|
297
|
-
? (icons?.micOn ?? <MicOnIcon color={TEXT_ON_DARK} />)
|
|
298
|
-
: (icons?.micOff ?? <MicOffIcon color={TEXT_ON_DARK} />)}
|
|
299
|
-
</TouchableOpacity>
|
|
300
|
-
|
|
301
|
-
{isVideo && (
|
|
302
|
-
<TouchableOpacity
|
|
303
|
-
accessibilityRole="button"
|
|
304
|
-
accessibilityLabel={
|
|
305
|
-
cameraOn ? t('call.action.stopCamera') : t('call.action.startCamera')
|
|
306
|
-
}
|
|
307
|
-
onPress={toggleCallCamera}
|
|
308
|
-
style={[styles.controlButton, { backgroundColor: cameraOn ? CONTROL_IDLE : DANGER }]}
|
|
309
|
-
>
|
|
310
|
-
{cameraOn
|
|
311
|
-
? (icons?.cameraOn ?? <CameraOnIcon color={TEXT_ON_DARK} />)
|
|
312
|
-
: (icons?.cameraOff ?? <CameraOffIcon color={TEXT_ON_DARK} />)}
|
|
313
|
-
</TouchableOpacity>
|
|
314
|
-
)}
|
|
315
|
-
|
|
316
|
-
{isVideo && cameraOn && (
|
|
317
|
-
<TouchableOpacity
|
|
318
|
-
accessibilityRole="button"
|
|
319
|
-
accessibilityLabel={t('call.action.switchCamera')}
|
|
320
|
-
onPress={switchCallCamera}
|
|
321
|
-
style={[styles.controlButton, { backgroundColor: CONTROL_IDLE }]}
|
|
322
|
-
>
|
|
323
|
-
<SwitchCameraIcon color={TEXT_ON_DARK} />
|
|
324
|
-
</TouchableOpacity>
|
|
325
|
-
)}
|
|
326
|
-
|
|
327
|
-
<TouchableOpacity
|
|
328
|
-
accessibilityRole="button"
|
|
329
|
-
accessibilityLabel={speakerOn ? t('call.action.speakerOff') : t('call.action.speakerOn')}
|
|
330
|
-
onPress={toggleCallSpeaker}
|
|
331
|
-
style={[styles.controlButton, { backgroundColor: CONTROL_IDLE }]}
|
|
332
|
-
>
|
|
333
|
-
{speakerOn ? <SpeakerIcon color={TEXT_ON_DARK} /> : <SpeakerOffIcon color={TEXT_ON_DARK} />}
|
|
334
|
-
</TouchableOpacity>
|
|
335
|
-
|
|
336
|
-
<TouchableOpacity
|
|
337
|
-
accessibilityRole="button"
|
|
338
|
-
accessibilityLabel={t('call.action.hangUp')}
|
|
339
|
-
onPress={onHangup}
|
|
340
|
-
style={[styles.controlButton, styles.hangupButton]}
|
|
341
|
-
>
|
|
342
|
-
{icons?.hangup ?? <HangUpIcon color={TEXT_ON_DARK} />}
|
|
343
|
-
</TouchableOpacity>
|
|
344
|
-
</View>
|
|
279
|
+
<CallControlBar
|
|
280
|
+
session={session}
|
|
281
|
+
isVideo={isVideo}
|
|
282
|
+
icons={icons}
|
|
283
|
+
onHangup={onHangup}
|
|
284
|
+
/>
|
|
345
285
|
</View>
|
|
346
286
|
);
|
|
347
287
|
};
|
|
@@ -454,24 +394,6 @@ const styles = StyleSheet.create({
|
|
|
454
394
|
fitButton: {
|
|
455
395
|
left: 60,
|
|
456
396
|
},
|
|
457
|
-
controls: {
|
|
458
|
-
flexDirection: 'row',
|
|
459
|
-
alignItems: 'center',
|
|
460
|
-
justifyContent: 'center',
|
|
461
|
-
gap: 16,
|
|
462
|
-
paddingTop: 20,
|
|
463
|
-
paddingHorizontal: 16,
|
|
464
|
-
},
|
|
465
|
-
controlButton: {
|
|
466
|
-
width: 56,
|
|
467
|
-
height: 56,
|
|
468
|
-
borderRadius: 28,
|
|
469
|
-
alignItems: 'center',
|
|
470
|
-
justifyContent: 'center',
|
|
471
|
-
},
|
|
472
|
-
hangupButton: {
|
|
473
|
-
backgroundColor: DANGER,
|
|
474
|
-
},
|
|
475
397
|
errorText: {
|
|
476
398
|
color: TEXT_ON_DARK,
|
|
477
399
|
fontSize: 14,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '../../networking/callTokenStanza';
|
|
9
9
|
import { translateKey } from '../../i18n/strings';
|
|
10
10
|
import { getGlobalXmppClient } from '../../utils/clientRegistry';
|
|
11
|
+
import { reportDeclineIfP2P } from '../../calls/declineReport';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Call teardown that doesn't need a mounted component.
|
|
@@ -48,6 +49,7 @@ export const terminateActiveCall = (state: CallSignalState): void => {
|
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
export const declineActiveCall = (): void => {
|
|
52
|
+
reportDeclineIfP2P(store.getState().call);
|
|
51
53
|
sendCallStateSignal('declined');
|
|
52
54
|
store.dispatch(declineIncomingCall());
|
|
53
55
|
};
|
|
@@ -62,6 +64,7 @@ const DECLINE_SIGNAL_POLL_MS = 500;
|
|
|
62
64
|
*/
|
|
63
65
|
export const declineActiveCallWhenOnline = (): void => {
|
|
64
66
|
const call = store.getState().call;
|
|
67
|
+
reportDeclineIfP2P(call);
|
|
65
68
|
const signal = {
|
|
66
69
|
peerXmppUsername: call.peerXmppUsername,
|
|
67
70
|
callId: call.callId,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Seconds since the call truly connected, counted from the server-set
|
|
5
|
+
* `joinedAt` anchor so both sides show the same time (web's
|
|
6
|
+
* useSharedCallElapsed). Null until the peer is in.
|
|
7
|
+
*/
|
|
8
|
+
export const useSharedCallElapsed = (anchorAt: number | null): number | null => {
|
|
9
|
+
const [, tick] = useState(0);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (anchorAt === null) {return;}
|
|
12
|
+
const id = setInterval(() => tick((n) => n + 1), 1000);
|
|
13
|
+
return () => clearInterval(id);
|
|
14
|
+
}, [anchorAt]);
|
|
15
|
+
if (anchorAt === null) {return null;}
|
|
16
|
+
return Math.max(0, Math.floor((Date.now() - anchorAt) / 1000));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const formatCallDuration = (totalSeconds: number): string => {
|
|
20
|
+
const safe = Math.max(0, Math.floor(totalSeconds));
|
|
21
|
+
const hours = Math.floor(safe / 3600);
|
|
22
|
+
const minutes = Math.floor((safe % 3600) / 60);
|
|
23
|
+
const seconds = safe % 60;
|
|
24
|
+
const mm = String(minutes).padStart(2, '0');
|
|
25
|
+
const ss = String(seconds).padStart(2, '0');
|
|
26
|
+
return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
|
|
27
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CallState } from '../../roomStore/callSlice';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* True while a call is far enough along to have a media session: connecting
|
|
5
|
+
* or in the call. A LiveKit call also needs its token (that is what the room
|
|
6
|
+
* is joined with); a peer to peer call has none, its media is the peer
|
|
7
|
+
* connection, so the phase alone decides.
|
|
8
|
+
*/
|
|
9
|
+
export const callCanRenderSession = (
|
|
10
|
+
call: Pick<CallState, 'phase' | 'mode' | 'token'>
|
|
11
|
+
): boolean =>
|
|
12
|
+
(call.phase === 'connecting' || call.phase === 'in-call') &&
|
|
13
|
+
(call.mode === 'p2p' || !!call.token);
|
|
@@ -4,6 +4,21 @@ import { store } from '../../roomStore';
|
|
|
4
4
|
import { setCallError, setCallPhase } from '../../roomStore/callSlice';
|
|
5
5
|
import { ensureLiveKitGlobals, loadLiveKit } from './livekitRuntime';
|
|
6
6
|
import { terminateActiveCall, translateCallString } from './callActions';
|
|
7
|
+
import {
|
|
8
|
+
getP2PSessionSnapshot,
|
|
9
|
+
isP2PSessionActive,
|
|
10
|
+
setP2PMicEnabled,
|
|
11
|
+
switchP2PCamera,
|
|
12
|
+
toggleP2PCamera,
|
|
13
|
+
toggleP2PSpeaker,
|
|
14
|
+
dismissP2PDeviceError,
|
|
15
|
+
useP2PSession,
|
|
16
|
+
} from './p2pCallSession';
|
|
17
|
+
import {
|
|
18
|
+
CallParticipantView,
|
|
19
|
+
CallSessionSnapshot,
|
|
20
|
+
IDLE_SESSION,
|
|
21
|
+
} from './callSessionTypes';
|
|
7
22
|
|
|
8
23
|
/**
|
|
9
24
|
* The LiveKit connection of the current call, owned outside React.
|
|
@@ -22,64 +37,20 @@ import { terminateActiveCall, translateCallString } from './callActions';
|
|
|
22
37
|
* only read the snapshot and call the controls.
|
|
23
38
|
*/
|
|
24
39
|
|
|
25
|
-
export
|
|
26
|
-
/** A LiveKit room exists for the current call. */
|
|
27
|
-
active: boolean;
|
|
28
|
-
connected: boolean;
|
|
29
|
-
/** Translated, dismissible camera/mic problem. Never fails the call. */
|
|
30
|
-
deviceError: string | null;
|
|
31
|
-
micOn: boolean;
|
|
32
|
-
cameraOn: boolean;
|
|
33
|
-
speakerOn: boolean;
|
|
34
|
-
/** Which camera the local video comes from; only the front one is mirrored. */
|
|
35
|
-
facingMode: 'user' | 'environment';
|
|
36
|
-
/**
|
|
37
|
-
* Bumped whenever the local camera track is re-acquired, so the self view
|
|
38
|
-
* can remount onto the new stream instead of holding the stopped one.
|
|
39
|
-
*/
|
|
40
|
-
localVideoEpoch: number;
|
|
41
|
-
/** `{ participant, publication, source }` for `<VideoTrack />`, live and unmuted only. */
|
|
42
|
-
remoteVideo: any;
|
|
43
|
-
localVideo: any;
|
|
44
|
-
remoteName: string | null;
|
|
45
|
-
remoteJoined: boolean;
|
|
46
|
-
/** Peer joined but has no live microphone. */
|
|
47
|
-
remoteMicMuted: boolean;
|
|
48
|
-
/**
|
|
49
|
-
* When the call truly connected: the later of the two LiveKit `joinedAt`
|
|
50
|
-
* stamps. The server sets them, so both sides count from the same moment
|
|
51
|
-
* (web's useSharedCallElapsed). Null until the peer is in.
|
|
52
|
-
*/
|
|
53
|
-
anchorAt: number | null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const IDLE: CallSessionSnapshot = {
|
|
57
|
-
active: false,
|
|
58
|
-
connected: false,
|
|
59
|
-
deviceError: null,
|
|
60
|
-
micOn: true,
|
|
61
|
-
cameraOn: false,
|
|
62
|
-
speakerOn: false,
|
|
63
|
-
facingMode: 'user',
|
|
64
|
-
localVideoEpoch: 0,
|
|
65
|
-
remoteVideo: null,
|
|
66
|
-
localVideo: null,
|
|
67
|
-
remoteName: null,
|
|
68
|
-
remoteJoined: false,
|
|
69
|
-
remoteMicMuted: false,
|
|
70
|
-
anchorAt: null,
|
|
71
|
-
};
|
|
40
|
+
export type { CallParticipantView, CallSessionSnapshot } from './callSessionTypes';
|
|
72
41
|
|
|
73
42
|
interface Session {
|
|
74
43
|
key: string;
|
|
75
44
|
room: any;
|
|
76
45
|
kind: 'audio' | 'video';
|
|
46
|
+
/** Group room call: many participants, the call outlives any one leaving. */
|
|
47
|
+
isGroup: boolean;
|
|
77
48
|
disposed: boolean;
|
|
78
49
|
everJoined: boolean;
|
|
79
50
|
}
|
|
80
51
|
|
|
81
52
|
let session: Session | null = null;
|
|
82
|
-
let snapshot: CallSessionSnapshot =
|
|
53
|
+
let snapshot: CallSessionSnapshot = IDLE_SESSION;
|
|
83
54
|
const listeners = new Set<() => void>();
|
|
84
55
|
let storeUnsubscribe: (() => void) | null = null;
|
|
85
56
|
|
|
@@ -97,9 +68,20 @@ const subscribe = (listener: () => void) => {
|
|
|
97
68
|
|
|
98
69
|
const getSnapshot = () => snapshot;
|
|
99
70
|
|
|
100
|
-
|
|
71
|
+
const useLiveKitSession = (): CallSessionSnapshot =>
|
|
101
72
|
useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
102
73
|
|
|
74
|
+
/**
|
|
75
|
+
* The media session of the current call, whichever transport carries it: a
|
|
76
|
+
* peer to peer connection (p2pCallSession.ts) when one is bound, else the
|
|
77
|
+
* LiveKit room. Callers never need to tell them apart.
|
|
78
|
+
*/
|
|
79
|
+
export const useCallSession = (): CallSessionSnapshot => {
|
|
80
|
+
const livekit = useLiveKitSession();
|
|
81
|
+
const p2p = useP2PSession();
|
|
82
|
+
return p2p.active ? p2p : livekit;
|
|
83
|
+
};
|
|
84
|
+
|
|
103
85
|
const sessionKey = (livekitUrl: string, token: string) => `${livekitUrl}|${token}`;
|
|
104
86
|
|
|
105
87
|
// A device (camera/mic) permission/availability problem during enable,
|
|
@@ -134,6 +116,32 @@ const joinedAtMs = (participant: any): number => {
|
|
|
134
116
|
return Number.isFinite(ms) ? ms : 0;
|
|
135
117
|
};
|
|
136
118
|
|
|
119
|
+
const micMutedOf = (participant: any, Track: any): boolean => {
|
|
120
|
+
const mic = participant?.getTrackPublication?.(Track.Source.Microphone);
|
|
121
|
+
return !mic || mic.isMuted !== false;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Everyone in a group call: us first, then the others in join order.
|
|
125
|
+
const groupParticipants = (room: any, Track: any): CallParticipantView[] => {
|
|
126
|
+
const speaking = new Set<string>(
|
|
127
|
+
(Array.from(room.activeSpeakers || []) as any[]).map((p) => p?.identity)
|
|
128
|
+
);
|
|
129
|
+
const view = (p: any, isLocal: boolean): CallParticipantView => ({
|
|
130
|
+
identity: String(p?.identity || ''),
|
|
131
|
+
name: String(p?.name || p?.identity || ''),
|
|
132
|
+
isLocal,
|
|
133
|
+
video: liveVideoRef(p, Track),
|
|
134
|
+
micMuted: micMutedOf(p, Track),
|
|
135
|
+
speaking: speaking.has(p?.identity),
|
|
136
|
+
});
|
|
137
|
+
return [
|
|
138
|
+
view(room.localParticipant, true),
|
|
139
|
+
...(Array.from(room.remoteParticipants?.values?.() || []) as any[]).map((p) =>
|
|
140
|
+
view(p, false)
|
|
141
|
+
),
|
|
142
|
+
];
|
|
143
|
+
};
|
|
144
|
+
|
|
137
145
|
const resync = () => {
|
|
138
146
|
const current = session;
|
|
139
147
|
const runtime = loadLiveKit();
|
|
@@ -148,6 +156,31 @@ const resync = () => {
|
|
|
148
156
|
const localJoined = joinedAtMs(room.localParticipant);
|
|
149
157
|
const remoteJoinedAt = joinedAtMs(remote);
|
|
150
158
|
|
|
159
|
+
if (current.isGroup) {
|
|
160
|
+
// A group call began when the first two were in; later joiners do not
|
|
161
|
+
// restart the clock (web's useSharedCallElapsed(group)).
|
|
162
|
+
const joins = [
|
|
163
|
+
localJoined,
|
|
164
|
+
...(Array.from(room.remoteParticipants?.values?.() || []) as any[]).map(
|
|
165
|
+
joinedAtMs
|
|
166
|
+
),
|
|
167
|
+
].filter((ms) => ms > 0);
|
|
168
|
+
emit({
|
|
169
|
+
remoteJoined: !!remote,
|
|
170
|
+
remoteName: null,
|
|
171
|
+
remoteVideo: null,
|
|
172
|
+
localVideo: liveVideoRef(room.localParticipant, Track),
|
|
173
|
+
remoteMicMuted: false,
|
|
174
|
+
participants: groupParticipants(room, Track),
|
|
175
|
+
anchorAt: remote
|
|
176
|
+
? joins.length >= 2
|
|
177
|
+
? Math.min(...joins)
|
|
178
|
+
: snapshot.anchorAt ?? Date.now()
|
|
179
|
+
: null,
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
151
184
|
emit({
|
|
152
185
|
remoteJoined: !!remote,
|
|
153
186
|
remoteName: remote?.name || remote?.identity || null,
|
|
@@ -179,7 +212,7 @@ export const stopCallSession = (): void => {
|
|
|
179
212
|
} catch {
|
|
180
213
|
// Never let teardown throw.
|
|
181
214
|
}
|
|
182
|
-
snapshot =
|
|
215
|
+
snapshot = IDLE_SESSION;
|
|
183
216
|
listeners.forEach((listener) => listener());
|
|
184
217
|
};
|
|
185
218
|
|
|
@@ -202,6 +235,8 @@ export interface StartCallSessionOptions {
|
|
|
202
235
|
token: string;
|
|
203
236
|
livekitUrl: string;
|
|
204
237
|
kind: 'audio' | 'video';
|
|
238
|
+
/** Group room call: show everyone, and keep going while others remain. */
|
|
239
|
+
isGroup?: boolean;
|
|
205
240
|
startWithMicOn?: boolean;
|
|
206
241
|
startWithCameraOn?: boolean;
|
|
207
242
|
}
|
|
@@ -214,6 +249,7 @@ export const startCallSession = ({
|
|
|
214
249
|
token,
|
|
215
250
|
livekitUrl,
|
|
216
251
|
kind,
|
|
252
|
+
isGroup = false,
|
|
217
253
|
startWithMicOn = true,
|
|
218
254
|
startWithCameraOn = true,
|
|
219
255
|
}: StartCallSessionOptions): void => {
|
|
@@ -251,11 +287,19 @@ export const startCallSession = ({
|
|
|
251
287
|
}
|
|
252
288
|
: {}),
|
|
253
289
|
});
|
|
254
|
-
const current: Session = {
|
|
290
|
+
const current: Session = {
|
|
291
|
+
key,
|
|
292
|
+
room,
|
|
293
|
+
kind,
|
|
294
|
+
isGroup,
|
|
295
|
+
disposed: false,
|
|
296
|
+
everJoined: false,
|
|
297
|
+
};
|
|
255
298
|
session = current;
|
|
256
299
|
snapshot = {
|
|
257
|
-
...
|
|
300
|
+
...IDLE_SESSION,
|
|
258
301
|
active: true,
|
|
302
|
+
isGroup,
|
|
259
303
|
micOn: startWithMicOn,
|
|
260
304
|
cameraOn: kind === 'video' && startWithCameraOn,
|
|
261
305
|
// Video calls belong on the loudspeaker, audio calls on the earpiece.
|
|
@@ -277,6 +321,7 @@ export const startCallSession = ({
|
|
|
277
321
|
.on(RoomEvent.TrackMuted, onChange)
|
|
278
322
|
.on(RoomEvent.TrackUnmuted, onChange)
|
|
279
323
|
.on(RoomEvent.ParticipantConnected, onChange)
|
|
324
|
+
.on(RoomEvent.ActiveSpeakersChanged, onChange)
|
|
280
325
|
.on(RoomEvent.MediaDevicesError, (error: unknown) => {
|
|
281
326
|
if (current.disposed) {return;}
|
|
282
327
|
emit({ deviceError: humanizeDeviceError(error) });
|
|
@@ -286,8 +331,13 @@ export const startCallSession = ({
|
|
|
286
331
|
resync();
|
|
287
332
|
// 1:1 semantics, as web's PeerLeaveWatcher: once the peer had been
|
|
288
333
|
// in the room, their leaving IS the end of the call. Before they
|
|
289
|
-
// join (outgoing "Ringing...") an empty room is expected.
|
|
290
|
-
|
|
334
|
+
// join (outgoing "Ringing...") an empty room is expected. A group
|
|
335
|
+
// call goes on while anyone is left, and ends when we hang up.
|
|
336
|
+
if (
|
|
337
|
+
!current.isGroup &&
|
|
338
|
+
current.everJoined &&
|
|
339
|
+
(room.remoteParticipants?.size ?? 0) === 0
|
|
340
|
+
) {
|
|
291
341
|
terminateActiveCall('ended');
|
|
292
342
|
}
|
|
293
343
|
})
|
|
@@ -363,10 +413,19 @@ export const startCallSession = ({
|
|
|
363
413
|
|
|
364
414
|
// ---- controls -----------------------------------------------------------
|
|
365
415
|
|
|
366
|
-
|
|
416
|
+
// The controls act on whichever transport carries the call.
|
|
417
|
+
const currentSnapshot = (): CallSessionSnapshot =>
|
|
418
|
+
isP2PSessionActive() ? getP2PSessionSnapshot() : snapshot;
|
|
419
|
+
|
|
420
|
+
export const toggleCallMic = (): Promise<void> =>
|
|
421
|
+
setCallMicEnabled(!currentSnapshot().micOn);
|
|
367
422
|
|
|
368
423
|
/** Set the mic to a given state (system call UI reports absolute values). */
|
|
369
424
|
export const setCallMicEnabled = async (next: boolean): Promise<void> => {
|
|
425
|
+
if (isP2PSessionActive()) {
|
|
426
|
+
setP2PMicEnabled(next);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
370
429
|
const current = session;
|
|
371
430
|
if (!current || snapshot.micOn === next) {return;}
|
|
372
431
|
emit({ micOn: next });
|
|
@@ -380,6 +439,10 @@ export const setCallMicEnabled = async (next: boolean): Promise<void> => {
|
|
|
380
439
|
};
|
|
381
440
|
|
|
382
441
|
export const toggleCallCamera = async (): Promise<void> => {
|
|
442
|
+
if (isP2PSessionActive()) {
|
|
443
|
+
await toggleP2PCamera();
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
383
446
|
const current = session;
|
|
384
447
|
if (!current) {return;}
|
|
385
448
|
const next = !snapshot.cameraOn;
|
|
@@ -397,6 +460,10 @@ export const toggleCallCamera = async (): Promise<void> => {
|
|
|
397
460
|
let switchingCamera = false;
|
|
398
461
|
|
|
399
462
|
export const switchCallCamera = async (): Promise<void> => {
|
|
463
|
+
if (isP2PSessionActive()) {
|
|
464
|
+
switchP2PCamera();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
400
467
|
const current = session;
|
|
401
468
|
const runtime = loadLiveKit();
|
|
402
469
|
if (!current || !runtime || switchingCamera) {return;}
|
|
@@ -437,6 +504,10 @@ export const switchCallCamera = async (): Promise<void> => {
|
|
|
437
504
|
};
|
|
438
505
|
|
|
439
506
|
export const toggleCallSpeaker = async (): Promise<void> => {
|
|
507
|
+
if (isP2PSessionActive()) {
|
|
508
|
+
await toggleP2PSpeaker();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
440
511
|
const runtime = loadLiveKit();
|
|
441
512
|
if (!session || !runtime) {return;}
|
|
442
513
|
const next = !snapshot.speakerOn;
|
|
@@ -459,6 +530,10 @@ export const toggleCallSpeaker = async (): Promise<void> => {
|
|
|
459
530
|
};
|
|
460
531
|
|
|
461
532
|
export const dismissCallDeviceError = (): void => {
|
|
533
|
+
if (isP2PSessionActive()) {
|
|
534
|
+
dismissP2PDeviceError();
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
462
537
|
emit({ deviceError: null });
|
|
463
538
|
};
|
|
464
539
|
|