react-softphone-lite
Version:
Webrtc Softphone React
302 lines (286 loc) • 7.99 kB
JavaScript
import { UA, debug } from 'jssip'
import _ from 'lodash'
function CallsFlowControl() {
this.onUserAgentAction = () => {
}
this.notify = (message) => {
this.onCallActionConnection('notify', message)
}
this.tmpEvent = () => {
console.log(this.activeCall)
console.log(this.callsQueue)
console.log(this.holdCallsQueue)
}
this.onCallActionConnection = () => {
}
this.engineEvent = () => {
}
this.setMicMuted = () => {
if (this.micMuted && this.activeCall) {
this.activeCall.unmute()
this.micMuted = false
this.onCallActionConnection('unmute', this.activeCall.id)
} else if (!this.micMuted && this.activeCall) {
this.micMuted = true
this.activeCall.mute()
this.onCallActionConnection('mute', this.activeCall.id)
}
}
this.hold = (sessionId) => {
// If there is an active call with id that is requested then fire hold
if (this.activeCall.id === sessionId) {
this.activeCall.hold()
}
}
this.unhold = (sessionId) => {
// If we dont have active call then unhold the the call with requested id
if (!this.activeCall) {
// Find the Requested call in hold calls array
const toUnhold = _.find(this.holdCallsQueue, { id: sessionId })
// If we found the call in hold calls array the fire unhold function
if (toUnhold) {
toUnhold.unhold()
}
} else {
console.log('Please exit from all active calls to unhold')
}
}
this.micMuted = false
this.activeCall = null
this.activeChanel = null
this.callsQueue = []
this.holdCallsQueue = []
this.player = {}
this.ringer = null
this.connectedPhone = null
this.config = {}
this.initiated = false
this.playRing = () => {
this.ringer.current.currentTime = 0
this.ringer.current.play()
}
this.stopRing = () => {
this.ringer.current.currentTime = 0
this.ringer.current.pause()
}
this.removeCallFromQueue = (callId) => {
_.remove(this.callsQueue, (calls) => calls.id === callId)
}
this.addCallToHoldQueue = (callId) => {
if (this.activeCall.id === callId) {
this.holdCallsQueue.push(this.activeCall)
}
}
this.removeCallFromActiveCall = (callId) => {
if (this.activeCall && callId === this.activeCall.id) {
this.activeCall = null
}
}
this.removeCallFromHoldQueue = (callId) => {
_.remove(this.holdCallsQueue, (calls) => calls.id === callId)
}
this.connectAudio = () => {
this.activeCall.connection.addEventListener('addstream', (event) => {
this.player.current.srcObject = event.stream
})
}
this.sessionEvent = (type, data, cause, callId) => {
// console.log(`Session: ${type}`);
// console.log('Data: ', data);
// console.log('callid: ', callId);
switch (type) {
case 'terminated':
// this.endCall(data, cause);
break
case 'accepted':
// this.startCall(data);
break
case 'reinvite':
this.onCallActionConnection('reinvite', callId, data)
break
case 'hold':
this.onCallActionConnection('hold', callId)
this.addCallToHoldQueue(callId)
this.removeCallFromActiveCall(callId)
break
case 'unhold':
this.onCallActionConnection('unhold', callId)
this.activeCall = _.find(this.holdCallsQueue, { id: callId })
this.removeCallFromHoldQueue(callId)
break
case 'dtmf':
break
case 'muted':
this.onCallActionConnection('muted', callId)
break
case 'unmuted':
break
case 'confirmed':
if (!this.activeCall) {
this.activeCall = _.find(this.callsQueue, { id: callId })
}
this.removeCallFromQueue(callId)
this.onCallActionConnection('callAccepted', callId, this.activeCall)
break
case 'connecting':
break
case 'ended':
this.onCallActionConnection('callEnded', callId)
this.removeCallFromQueue(callId)
this.removeCallFromActiveCall(callId)
this.removeCallFromHoldQueue(callId)
if (this.callsQueue.length === 0) {
this.stopRing()
}
break
case 'failed':
this.onCallActionConnection('callEnded', callId)
this.removeCallFromQueue(callId)
this.removeCallFromActiveCall(callId)
if (this.callsQueue.length === 0) {
this.stopRing()
}
break
default:
break
}
}
this.handleNewRTCSession = (rtcPayload) => {
const { session: call } = rtcPayload
if (call.direction === 'incoming') {
this.callsQueue.push(call)
this.onCallActionConnection('incomingCall', call)
if (!this.activeCall) {
this.playRing()
}
} else {
this.activeCall = call
this.onCallActionConnection('outgoingCall', call)
this.connectAudio()
}
const defaultCallEventsToHandle = [
'peerconnection',
'connecting',
'sending',
'progress',
'accepted',
'newDTMF',
'newInfo',
'hold',
'unhold',
'muted',
'unmuted',
'reinvite',
'update',
'refer',
'replaces',
'sdp',
'icecandidate',
'getusermediafailed',
'ended',
'failed',
'connecting',
'confirmed'
]
_.forEach(defaultCallEventsToHandle, (eventType) => {
call.on(eventType, (data, cause) => {
this.sessionEvent(eventType, data, cause, call.id)
})
})
}
this.init = () => {
try {
this.phone = new UA(this.config)
this.phone.on('newRTCSession', this.handleNewRTCSession.bind(this))
const binds = [
'connected',
'disconnected',
'registered',
'unregistered',
'registrationFailed',
'invite',
'message',
'connecting'
]
_.forEach(binds, (value) => {
this.phone.on(value, (e) => {
this.engineEvent(value, e)
})
})
this.initiated = true
} catch (e) {
console.log(e)
}
}
this.call = (to) => {
if (!this.connectedPhone) {
this.notify('Please connect to Voip Server in order to make calls')
console.log('User agent not registered yet')
return
}
if (this.activeCall) {
this.notify('Already have an active call')
console.log('Already has active call')
return
}
this.phone.call(`sip:${to}@${this.config.domain}`, {
extraHeaders: ['First: first', 'Second: second'],
RTCConstraints: {
optional: [{ DtlsSrtpKeyAgreement: 'true' }]
},
mediaConstraints: {
audio: true
},
sessionTimersExpires: 600
})
}
this.answer = (sessionId) => {
if (this.activeCall) {
this.notify('Already have an active call')
console.log('Already has active call')
} else if (this.activeChanel.inCall) {
this.notify('Current chanel is busy')
console.log('Chanel is Busy')
} else {
// Stop the annoying ring ring
this.stopRing()
// Get the call from calls Queue
this.activeCall = _.find(this.callsQueue, { id: sessionId })
if (this.activeCall) {
this.activeCall.customPayload = this.activeChanel.id
this.activeCall.answer({
mediaConstraints: {
audio: true
}
})
// Connect Audio
this.connectAudio()
}
}
}
this.hungup = (e) => {
try {
this.phone._sessions[e].terminate()
} catch (e) {
console.log(e)
console.log('Call already terminated')
}
}
this.start = () => {
if (!this.initiated) {
this.notify('Error: 356 Please report')
console.log('Please call .init() before connect')
return
}
if (this.config.debug) {
debug.enable('JsSIP:*')
} else {
debug.disable()
}
this.phone.start()
}
this.stop = () => {
this.phone.stop()
}
}
export default CallsFlowControl