@ -1,4 +1,8 @@
import _ from 'lodash' ;
import { ToastUtils } from '.' ;
import { SessionSettingCategory } from '../../components/session/settings/SessionSettings' ;
import { getConversationById } from '../../data/data' ;
import { MessageModelType } from '../../models/messageType' ;
import { SignalService } from '../../protobuf' ;
import {
answerCall ,
@ -7,6 +11,8 @@ import {
incomingCall ,
startingCallWith ,
} from '../../state/ducks/conversations' ;
import { SectionType , showLeftPaneSection , showSettingsSection } from '../../state/ducks/section' ;
import { getConversationController } from '../conversations' ;
import { CallMessage } from '../messages/outgoing/controlMessage/CallMessage' ;
import { ed25519Str } from '../onions/onionPath' ;
import { getMessageQueue } from '../sending' ;
@ -33,6 +39,7 @@ const ENABLE_VIDEO = true;
let makingOffer = false ;
let ignoreOffer = false ;
let isSettingRemoteAnswerPending = false ;
let lastOutgoingOfferTimestamp = - Infinity ;
const configuration = {
configuration : {
@ -67,8 +74,10 @@ export async function USER_callRecipient(recipient: string) {
peerConnection ? . addTrack ( track , mediaDevices ) ;
} ) ;
} catch ( err ) {
console . error ( 'Failed to open media devices. Check camera and mic app permissions' ) ;
// TODO: implement toast popup
ToastUtils . pushMicAndCameraPermissionNeeded ( ( ) = > {
window . inboxStore ? . dispatch ( showLeftPaneSection ( SectionType . Settings ) ) ;
window . inboxStore ? . dispatch ( showSettingsSection ( SessionSettingCategory . Privacy ) ) ;
} ) ;
}
peerConnection . addEventListener ( 'connectionstatechange' , _event = > {
window . log . info ( 'peerConnection?.connectionState caller :' , peerConnection ? . connectionState ) ;
@ -92,21 +101,6 @@ export async function USER_callRecipient(recipient: string) {
console . warn ( 'negotiationneeded:' , event ) ;
try {
makingOffer = true ;
// const offerDescription = await peerConnection?.createOffer({
// offerToReceiveAudio: true,
// offerToReceiveVideo: true,
// });
// if (!offerDescription) {
// console.error('Failed to create offer for negotiation');
// return;
// }
// await peerConnection?.setLocalDescription(offerDescription);
// if (!offerDescription || !offerDescription.sdp || !offerDescription.sdp.length) {
// // window.log.warn(`failed to createOffer for recipient ${ed25519Str(recipient)}`);
// console.warn(`failed to createOffer for recipient ${ed25519Str(recipient)}`);
// return;
// }
// @ts-ignore
await peerConnection ? . setLocalDescription ( ) ;
let offer = await peerConnection ? . createOffer ( ) ;
@ -116,12 +110,20 @@ export async function USER_callRecipient(recipient: string) {
const callOfferMessage = new CallMessage ( {
timestamp : Date.now ( ) ,
type : SignalService . CallMessage . Type . OFFER ,
// sdps: [offerDescription.sdp],
sdps : [ offer . sdp ] ,
} ) ;
window . log . info ( 'sending OFFER MESSAGE' ) ;
await getMessageQueue ( ) . sendToPubKeyNonDurably ( PubKey . cast ( recipient ) , callOfferMessage ) ;
const sendResult = await getMessageQueue ( ) . sendToPubKeyNonDurably (
PubKey . cast ( recipient ) ,
callOfferMessage
) ;
if ( typeof sendResult === 'number' ) {
console . warn ( 'setting last sent timestamp' ) ;
lastOutgoingOfferTimestamp = sendResult ;
}
await new Promise ( r = > setTimeout ( r , 10000 ) ) ;
}
} catch ( err ) {
console . error ( err ) ;
@ -161,7 +163,14 @@ export async function USER_callRecipient(recipient: string) {
} ) ;
window . log . info ( 'sending OFFER MESSAGE' ) ;
await getMessageQueue ( ) . sendToPubKeyNonDurably ( PubKey . cast ( recipient ) , callOfferMessage ) ;
let sendResult = await getMessageQueue ( ) . sendToPubKeyNonDurably (
PubKey . cast ( recipient ) ,
callOfferMessage
) ;
if ( typeof sendResult === 'number' ) {
console . warn ( 'setting timestamp' ) ;
lastOutgoingOfferTimestamp = sendResult ;
}
// FIXME audric dispatch UI update to show the calling UI
}
@ -363,37 +372,30 @@ export function handleEndCallMessage(sender: string) {
export async function handleOfferCallMessage (
sender : string ,
callMessage : SignalService.CallMessage
callMessage : SignalService.CallMessage ,
incomingOfferTimestamp : number
) {
try {
console . warn ( { callMessage } ) ;
const convos = getConversationController ( ) . getConversations ( ) ;
if ( convos . some ( convo = > convo . callState !== undefined ) ) {
return await handleMissedCall ( sender , incomingOfferTimestamp ) ;
}
const readyForOffer =
! makingOffer && ( peerConnection ? . signalingState == 'stable' || isSettingRemoteAnswerPending ) ;
// TODO: How should politeness be decided between client / recipient?
ignoreOffer = ! true && ! readyForOffer ;
// TODO: however sent offer last is the impolite user
const polite = lastOutgoingOfferTimestamp < incomingOfferTimestamp ;
console . warn ( { polite } ) ;
ignoreOffer = ! polite && ! readyForOffer ;
console . warn ( { ignoreOffer } ) ;
if ( ignoreOffer ) {
// window.log?.warn('Received offer when unready for offer; Ignoring offer.');
console . warn ( 'Received offer when unready for offer; Ignoring offer.' ) ;
return ;
}
// const description = await peerConnection?.createOffer({
// const description = await peerConnection?.createOffer({
// offerToReceiveVideo: true,
// offerToReceiveAudio: true,
// })
// @ts-ignore
await peerConnection ? . setLocalDescription ( ) ;
console . warn ( peerConnection ? . localDescription ) ;
const message = new CallMessage ( {
type : SignalService . CallMessage . Type . ANSWER ,
timestamp : Date.now ( ) ,
} ) ;
await getMessageQueue ( ) . sendToPubKeyNonDurably ( PubKey . cast ( sender ) , message ) ;
// TODO: send via our signalling with the sdp of our pc.localDescription
// don't need to do the sending here as we dispatch an answer in a
} catch ( err ) {
window . log ? . error ( ` Error handling offer message ${ err } ` ) ;
}
@ -405,6 +407,25 @@ export async function handleOfferCallMessage(
window . inboxStore ? . dispatch ( incomingCall ( { pubkey : sender } ) ) ;
}
async function handleMissedCall ( sender : string , incomingOfferTimestamp : number ) {
const incomingCallConversation = await getConversationById ( sender ) ;
ToastUtils . pushedMissedCall ( incomingCallConversation ? . getNickname ( ) || 'Unknown' ) ;
await incomingCallConversation ? . addSingleMessage ( {
conversationId : incomingCallConversation.id ,
source : sender ,
type : 'incoming' as MessageModelType ,
sent_at : incomingOfferTimestamp ,
received_at : Date.now ( ) ,
expireTimer : 0 ,
body : 'Missed call' ,
unread : 1 ,
isCall : false ,
} ) ;
incomingCallConversation ? . updateLastMessage ( ) ;
return ;
}
export async function handleCallAnsweredMessage (
sender : string ,
callMessage : SignalService.CallMessage