|  |  |  | @ -469,12 +469,12 @@ const findLastMessageTypeFromSender = (sender: string, msgType: SignalService.Ca | 
		
	
		
			
				|  |  |  |  |   // FIXME this does not sort by timestamp as we do not have a timestamp stored in the SignalService.CallMessage object...
 | 
		
	
		
			
				|  |  |  |  |   const allMsg = _.flattenDeep([...msgCacheFromSenderWithDevices.values()]); | 
		
	
		
			
				|  |  |  |  |   const allMsgFromType = allMsg.filter(m => m.type === msgType); | 
		
	
		
			
				|  |  |  |  |   const lastOfferMessage = _.last(allMsgFromType); | 
		
	
		
			
				|  |  |  |  |   const lastMessageOfType = _.last(allMsgFromType); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if (!lastOfferMessage) { | 
		
	
		
			
				|  |  |  |  |   if (!lastMessageOfType) { | 
		
	
		
			
				|  |  |  |  |     return undefined; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   return lastOfferMessage; | 
		
	
		
			
				|  |  |  |  |   return lastMessageOfType; | 
		
	
		
			
				|  |  |  |  | }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | function handleSignalingStateChangeEvent() { | 
		
	
	
		
			
				
					|  |  |  | @ -486,7 +486,7 @@ function handleSignalingStateChangeEvent() { | 
		
	
		
			
				|  |  |  |  | function handleConnectionStateChanged(pubkey: string) { | 
		
	
		
			
				|  |  |  |  |   window.log.info('handleConnectionStateChanged :', peerConnection?.connectionState); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if (peerConnection?.signalingState === 'closed') { | 
		
	
		
			
				|  |  |  |  |   if (peerConnection?.signalingState === 'closed' || peerConnection?.connectionState === 'failed') { | 
		
	
		
			
				|  |  |  |  |     closeVideoCall(); | 
		
	
		
			
				|  |  |  |  |   } else if (peerConnection?.connectionState === 'connected') { | 
		
	
		
			
				|  |  |  |  |     setIsRinging(false); | 
		
	
	
		
			
				
					|  |  |  | @ -496,6 +496,7 @@ function handleConnectionStateChanged(pubkey: string) { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | function closeVideoCall() { | 
		
	
		
			
				|  |  |  |  |   window.log.info('closingVideoCall '); | 
		
	
		
			
				|  |  |  |  |   setIsRinging(false); | 
		
	
		
			
				|  |  |  |  |   if (peerConnection) { | 
		
	
		
			
				|  |  |  |  |     peerConnection.ontrack = null; | 
		
	
		
			
				|  |  |  |  |     peerConnection.onicecandidate = null; | 
		
	
	
		
			
				
					|  |  |  | @ -530,8 +531,14 @@ function closeVideoCall() { | 
		
	
		
			
				|  |  |  |  |   selectedCameraId = DEVICE_DISABLED_DEVICE_ID; | 
		
	
		
			
				|  |  |  |  |   selectedAudioInputId = DEVICE_DISABLED_DEVICE_ID; | 
		
	
		
			
				|  |  |  |  |   currentCallUUID = undefined; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   window.inboxStore?.dispatch(setFullScreenCall(false)); | 
		
	
		
			
				|  |  |  |  |   remoteVideoStreamIsMuted = true; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   makingOffer = false; | 
		
	
		
			
				|  |  |  |  |   ignoreOffer = false; | 
		
	
		
			
				|  |  |  |  |   isSettingRemoteAnswerPending = false; | 
		
	
		
			
				|  |  |  |  |   lastOutgoingOfferTimestamp = -Infinity; | 
		
	
		
			
				|  |  |  |  |   callVideoListeners(); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -552,7 +559,7 @@ function onDataChannelReceivedMessage(ev: MessageEvent<string>) { | 
		
	
		
			
				|  |  |  |  |       if (!foundEntry || !foundEntry.id) { | 
		
	
		
			
				|  |  |  |  |         return; | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |       handleCallTypeEndCall(foundEntry.id); | 
		
	
		
			
				|  |  |  |  |       handleCallTypeEndCall(foundEntry.id, currentCallUUID); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       return; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
	
		
			
				
					|  |  |  | @ -612,6 +619,24 @@ function createOrGetPeerConnection(withPubkey: string, isAcceptingCall = false) | 
		
	
		
			
				|  |  |  |  |     handleIceCandidates(event, withPubkey); | 
		
	
		
			
				|  |  |  |  |   }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   peerConnection.oniceconnectionstatechange = () => { | 
		
	
		
			
				|  |  |  |  |     window.log.info( | 
		
	
		
			
				|  |  |  |  |       'oniceconnectionstatechange peerConnection.iceConnectionState: ', | 
		
	
		
			
				|  |  |  |  |       peerConnection?.iceConnectionState | 
		
	
		
			
				|  |  |  |  |     ); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if (peerConnection && peerConnection?.iceConnectionState === 'disconnected') { | 
		
	
		
			
				|  |  |  |  |       //this will trigger a negotation event with iceRestart set to true in the createOffer options set
 | 
		
	
		
			
				|  |  |  |  |       global.setTimeout(() => { | 
		
	
		
			
				|  |  |  |  |         window.log.info('onconnectionstatechange disconnected: restartIce()'); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         if (peerConnection?.iceConnectionState === 'disconnected') { | 
		
	
		
			
				|  |  |  |  |           (peerConnection as any).restartIce(); | 
		
	
		
			
				|  |  |  |  |         } | 
		
	
		
			
				|  |  |  |  |       }, 2000); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   }; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return peerConnection; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -689,22 +714,31 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) { | 
		
	
		
			
				|  |  |  |  | // tslint:disable-next-line: function-name
 | 
		
	
		
			
				|  |  |  |  | export async function USER_rejectIncomingCallRequest(fromSender: string) { | 
		
	
		
			
				|  |  |  |  |   setIsRinging(false); | 
		
	
		
			
				|  |  |  |  |   const endCallMessage = new CallMessage({ | 
		
	
		
			
				|  |  |  |  |     type: SignalService.CallMessage.Type.END_CALL, | 
		
	
		
			
				|  |  |  |  |     timestamp: Date.now(), | 
		
	
		
			
				|  |  |  |  |     uuid: uuidv4(), // just send a random thing, we just want to reject the call
 | 
		
	
		
			
				|  |  |  |  |   }); | 
		
	
		
			
				|  |  |  |  |   // delete all msg not from that uuid only but from that sender pubkey
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   const lastOfferMessage = findLastMessageTypeFromSender( | 
		
	
		
			
				|  |  |  |  |     fromSender, | 
		
	
		
			
				|  |  |  |  |     SignalService.CallMessage.Type.OFFER | 
		
	
		
			
				|  |  |  |  |   ); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   const lastCallUUID = lastOfferMessage?.uuid; | 
		
	
		
			
				|  |  |  |  |   window.log.info(`USER_rejectIncomingCallRequest ${ed25519Str(fromSender)}: ${lastCallUUID}`); | 
		
	
		
			
				|  |  |  |  |   if (lastCallUUID) { | 
		
	
		
			
				|  |  |  |  |     const endCallMessage = new CallMessage({ | 
		
	
		
			
				|  |  |  |  |       type: SignalService.CallMessage.Type.END_CALL, | 
		
	
		
			
				|  |  |  |  |       timestamp: Date.now(), | 
		
	
		
			
				|  |  |  |  |       uuid: lastCallUUID, | 
		
	
		
			
				|  |  |  |  |     }); | 
		
	
		
			
				|  |  |  |  |     await getMessageQueue().sendToPubKeyNonDurably(PubKey.cast(fromSender), endCallMessage); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // delete all msg not from that uuid only but from that sender pubkey
 | 
		
	
		
			
				|  |  |  |  |     clearCallCacheFromPubkeyAndUUID(fromSender, lastCallUUID); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   window.inboxStore?.dispatch( | 
		
	
		
			
				|  |  |  |  |     endCall({ | 
		
	
		
			
				|  |  |  |  |       pubkey: fromSender, | 
		
	
		
			
				|  |  |  |  |     }) | 
		
	
		
			
				|  |  |  |  |   ); | 
		
	
		
			
				|  |  |  |  |   window.log.info('USER_rejectIncomingCallRequest'); | 
		
	
		
			
				|  |  |  |  |   clearCallCacheFromPubkey(fromSender); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   await getMessageQueue().sendToPubKeyNonDurably(PubKey.cast(fromSender), endCallMessage); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   const convos = getConversationController().getConversations(); | 
		
	
		
			
				|  |  |  |  |   const callingConvos = convos.filter(convo => convo.callState !== undefined); | 
		
	
	
		
			
				
					|  |  |  | @ -737,28 +771,18 @@ export async function USER_hangup(fromSender: string) { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   sendHangupViaDataChannel(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   clearCallCacheFromPubkey(fromSender); | 
		
	
		
			
				|  |  |  |  |   clearCallCacheFromPubkeyAndUUID(fromSender, currentCallUUID); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   const convos = getConversationController().getConversations(); | 
		
	
		
			
				|  |  |  |  |   const callingConvos = convos.filter(convo => convo.callState !== undefined); | 
		
	
		
			
				|  |  |  |  |   if (callingConvos.length > 0) { | 
		
	
		
			
				|  |  |  |  |     // we just got a new offer from someone we are already in a call with
 | 
		
	
		
			
				|  |  |  |  |     if (callingConvos.length === 1 && callingConvos[0].id === fromSender) { | 
		
	
		
			
				|  |  |  |  |       closeVideoCall(); | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   closeVideoCall(); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | export function handleCallTypeEndCall(sender: string) { | 
		
	
		
			
				|  |  |  |  |   clearCallCacheFromPubkey(sender); | 
		
	
		
			
				|  |  |  |  | export function handleCallTypeEndCall(sender: string, aboutCallUUID?: string) { | 
		
	
		
			
				|  |  |  |  |   window.log.info('handling callMessage END_CALL:', aboutCallUUID); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   window.log.info('handling callMessage END_CALL'); | 
		
	
		
			
				|  |  |  |  |   if (aboutCallUUID) { | 
		
	
		
			
				|  |  |  |  |     clearCallCacheFromPubkeyAndUUID(sender, aboutCallUUID); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   const convos = getConversationController().getConversations(); | 
		
	
		
			
				|  |  |  |  |   const callingConvos = convos.filter(convo => convo.callState !== undefined); | 
		
	
		
			
				|  |  |  |  |   if (callingConvos.length > 0) { | 
		
	
		
			
				|  |  |  |  |     // we just got a end call event from whoever we are in a call with
 | 
		
	
		
			
				|  |  |  |  |     if (callingConvos.length === 1 && callingConvos[0].id === sender) { | 
		
	
		
			
				|  |  |  |  |     if (aboutCallUUID === currentCallUUID) { | 
		
	
		
			
				|  |  |  |  |       closeVideoCall(); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |       window.inboxStore?.dispatch(endCall({ pubkey: sender })); | 
		
	
	
		
			
				
					|  |  |  | @ -983,8 +1007,8 @@ export async function handleOtherCallTypes(sender: string, callMessage: SignalSe | 
		
	
		
			
				|  |  |  |  |   pushCallMessageToCallCache(sender, remoteCallUUID, callMessage); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | function clearCallCacheFromPubkey(sender: string) { | 
		
	
		
			
				|  |  |  |  |   callCache.delete(sender); | 
		
	
		
			
				|  |  |  |  | function clearCallCacheFromPubkeyAndUUID(sender: string, callUUID: string) { | 
		
	
		
			
				|  |  |  |  |   callCache.get(sender)?.delete(callUUID); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | function createCallCacheForPubkeyAndUUID(sender: string, uuid: string) { | 
		
	
	
		
			
				
					|  |  |  | 
 |