TT#23572 Call: As a Customer, I want to be able to receive an audio/video call

Change-Id: Ifb0dc96cb06ef1720d36c42fb346f5f01e1d617a
changes/13/17313/6
Hans-Peter Herzog 8 years ago
parent e1689379b4
commit 3db9b924d7

@ -12,14 +12,18 @@
<span v-else-if="isInitiating" class="text">{{ $t('call.initiating') }}</span> <span v-else-if="isInitiating" class="text">{{ $t('call.initiating') }}</span>
<span v-else-if="isRinging" class="text">{{ $t('call.ringing') }}</span> <span v-else-if="isRinging" class="text">{{ $t('call.ringing') }}</span>
<span v-else-if="isEnded" class="text">{{ $t('call.ended') }}</span> <span v-else-if="isEnded" class="text">{{ $t('call.ended') }}</span>
<span v-else-if="isIncoming" class="text">Incoming call</span>
<span v-else class="text">{{ $t('call.call') }}</span> <span v-else class="text">{{ $t('call.call') }}</span>
<q-btn round small slot="right" class="no-shadow" @click="close()" icon="clear"/> <q-btn round small slot="right" class="no-shadow" @click="close()" icon="clear"/>
</q-card-title> </q-card-title>
<q-card-main> <q-card-main>
<div v-if="isTrying" class="csc-spinner"><q-spinner-rings color="primary" :size="60" /></div> <div v-if="isRinging" class="csc-spinner"><q-spinner-rings color="primary" :size="60" /></div>
<div v-if="isCalling" class="phone-number">{{ getNumber }}</div> <div v-if="!isPreparing" class="phone-number">{{ getNumber | numberFormat }}</div>
<csc-media v-if="isCalling && localMediaStream != null" :stream="localMediaStream" />
<csc-media id="local-media" v-show="isCalling" :stream="localMediaStream" />
<csc-media id="remote-media" v-show="isEstablished" :stream="remoteMediaStream" />
<q-field v-if="isPreparing" :helper="$t('call.inputNumber')" :error="validationEnabled && phoneNumberError" <q-field v-if="isPreparing" :helper="$t('call.inputNumber')" :error="validationEnabled && phoneNumberError"
:error-label="$t('call.inputValidNumber')" :count="64" dark> :error-label="$t('call.inputValidNumber')" :count="64" dark>
<q-input :float-label="$t('call.number')" v-model="formattedPhoneNumber" dark clearable max="64" <q-input :float-label="$t('call.number')" v-model="formattedPhoneNumber" dark clearable max="64"
@ -34,6 +38,9 @@
<q-btn v-if="isPreparing" round small color="primary" @click="call('audioVideo')" icon="videocam" /> <q-btn v-if="isPreparing" round small color="primary" @click="call('audioVideo')" icon="videocam" />
<q-btn v-if="isCalling" round small color="negative" @click="hangUp()" icon="call end" /> <q-btn v-if="isCalling" round small color="negative" @click="hangUp()" icon="call end" />
<q-btn v-if="isEnded" round small color="negative" @click="init()" icon="clear"/> <q-btn v-if="isEnded" round small color="negative" @click="init()" icon="clear"/>
<q-btn v-if="isIncoming" round small color="primary" @click="accept('audioOnly')" icon="mic" />
<q-btn v-if="isIncoming" round small color="primary" @click="accept('audioVideo')" icon="videocam" />
<q-btn v-if="isIncoming" round small color="negative" @click="decline()" icon="call end" />
</q-card-actions> </q-card-actions>
</q-card> </q-card>
@ -45,16 +52,14 @@
import { mapState, mapGetters } from 'vuex' import { mapState, mapGetters } from 'vuex'
import CscMedia from './CscMedia' import CscMedia from './CscMedia'
import { QLayout, QCard, QCardTitle, QCardSeparator, QCardMain, QField, QInput, import { QLayout, QCard, QCardTitle, QCardSeparator, QCardMain, QField, QInput,
QCardActions, QBtn, QIcon, Loading, Alert, QSpinnerRings } from 'quasar-framework' QCardActions, QBtn, QIcon, Loading, Alert, QSpinnerRings, Dialog } from 'quasar-framework'
import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber' import { normalizeNumber, rawNumber } from '../filters/number-format'
var phoneUtil = PhoneNumberUtil.getInstance();
export default { export default {
name: 'csc-call', name: 'csc-call',
props: ['region'], props: ['region'],
data () { data () {
return { return {
phoneNumber: '', phoneNumber: '',
parsedPhoneNumber: null,
phoneNumberError: false, phoneNumberError: false,
validationEnabled: false validationEnabled: false
} }
@ -79,12 +84,12 @@
QBtn, QBtn,
QIcon, QIcon,
QSpinnerRings, QSpinnerRings,
CscMedia CscMedia,
Dialog
}, },
methods: { methods: {
init() { init() {
this.phoneNumber = ''; this.phoneNumber = '';
this.parsedPhoneNumber = null;
this.validationEnabled = false; this.validationEnabled = false;
this.phoneNumberError = false; this.phoneNumberError = false;
this.$store.commit('call/inputNumber'); this.$store.commit('call/inputNumber');
@ -97,7 +102,7 @@
}, },
call(localMedia) { call(localMedia) {
this.validationEnabled = true; this.validationEnabled = true;
if(this.parsedPhoneNumber !== null) { if(this.phoneNumber !== null) {
this.phoneNumberError = false; this.phoneNumberError = false;
this.$store.dispatch('call/start', { this.$store.dispatch('call/start', {
number: this.phoneNumber, number: this.phoneNumber,
@ -107,12 +112,37 @@
this.phoneNumberError = true; this.phoneNumberError = true;
} }
}, },
accept(localMedia) {
this.$store.dispatch('call/accept', localMedia);
},
decline() {
this.hangUp();
this.$emit('close');
},
hangUp() { hangUp() {
this.$store.dispatch('call/hangUp'); this.$store.dispatch('call/hangUp');
}, },
close() { close() {
this.$store.commit('call/inputNumber'); if(this.isPreparing || this.isEnded) {
this.$emit('close'); this.init();
this.$emit('close');
} else {
Dialog.create({
title: this.$t('call.endCall'),
message: this.$t('call.endCallDialog'),
buttons: [
'Cancel',
{
label: this.$t('call.endCall'),
color: 'negative',
handler: ()=>{
this.hangUp();
this.$emit('close');
}
}
]
});
}
}, },
playIncomingSound() { playIncomingSound() {
this.$refs.incomingRinging.play(); this.$refs.incomingRinging.play();
@ -124,44 +154,26 @@
computed: { computed: {
formattedPhoneNumber: { formattedPhoneNumber: {
get() { get() {
if(this.parsedPhoneNumber !== null) { return normalizeNumber(this.phoneNumber);
return _.trim(phoneUtil.format(this.parsedPhoneNumber, PhoneNumberFormat.INTERNATIONAL));
} else {
return _.trim(this.phoneNumber);
}
}, },
set(value) { set(value) {
this.validationEnabled = true; this.validationEnabled = true;
this.phoneNumber = _.trim(value); this.phoneNumber = rawNumber(value);
if(this.phoneNumber.match('^[1-9]')) {
this.phoneNumber = '+' + this.phoneNumber;
} else if(this.phoneNumber === '+') {
this.phoneNumber = '';
}
if(phoneUtil.isPossibleNumberString(this.phoneNumber, this.region)) {
try {
this.parsedPhoneNumber = phoneUtil.parse(this.phoneNumber, this.region);
this.phoneNumber = phoneUtil.format(this.parsedPhoneNumber, PhoneNumberFormat.E164);
this.phoneNumberError = false;
} catch(err) {
this.parsedPhoneNumber = null;
this.phoneNumberError = true;
}
} else {
this.parsedPhoneNumber = null;
this.phoneNumberError = true;
}
} }
}, },
localMediaStream() { localMediaStream() {
if(this.$store.state.call.localMediaStream != null) { if(this.$store.state.call.localMediaStream !== null) {
return this.$store.state.call.localMediaStream.getStream(); return this.$store.state.call.localMediaStream.getStream();
} else { } else {
return null; return null;
} }
}, },
remoteMediaStream() { remoteMediaStream() {
console.log(this.$refs.remoteMedia); if(this.$store.state.call.remoteMediaStream !== null) {
return this.$store.state.call.remoteMediaStream.getStream();
} else {
return null;
}
}, },
...mapGetters('call', [ ...mapGetters('call', [
'isPreparing', 'isPreparing',
@ -170,6 +182,8 @@
'isRinging', 'isRinging',
'isCalling', 'isCalling',
'isEnded', 'isEnded',
'isIncoming',
'isEstablished',
'getNumber', 'getNumber',
'getMediaType', 'getMediaType',
'getLocalMediaType', 'getLocalMediaType',

@ -1,34 +1,65 @@
<template> <template>
<div class="csc-media"> <div class="csc-media">
<video ref="media"></video> <div v-show="loading" class="csc-spinner">
<q-spinner-mat color="primary" :size="60" />
</div>
<video v-show="!loading && hasVideo" ref="media" autoplay></video>
</div> </div>
</template> </template>
<script> <script>
import _ from 'lodash'; import _ from 'lodash';
import { QSpinnerMat } from 'quasar-framework'
export default { export default {
name: 'csc-media', name: 'csc-media',
props: ['stream'], props: ['stream'],
data () { data () {
return { return {
currentStream: this.stream currentStream: this.stream,
loading: true,
}
},
mounted() {},
components: {
QSpinnerMat
},
methods: {
assignStream(stream) {
this.currentStream = stream;
if(_.isObject(this.currentStream) && _.isObject(this.$refs.media) &&
!_.isUndefined(this.$refs.media.srcObject)) {
this.$refs.media.srcObject = this.currentStream;
} else if(_.isObject(this.currentStream) && _.isObject(this.$refs.media) &&
!_.isUndefined(this.$refs.media.mozSrcObject)) {
this.$refs.media.mozSrcObject = this.currentStream;
} else if(_.isObject(this.currentStream) && _.isObject(this.$refs.media) &&
_.isObject(URL) && _.isFunction(URL.createObjectURL)) {
this.$refs.media.src = URL.createObjectURL(this.currentStream);
}
let timer = setInterval(()=>{
if(this.$refs.media.currentTime > 0) {
this.loading = false;
clearInterval(timer);
}
}, 100);
} }
}, },
mounted() { watch: {
if(_.isObject(this.currentStream) && _.isObject(this.$refs.media) && stream() {
!_.isUndefined(this.$refs.media.srcObject)) { if(_.isObject(this.stream) && this.currentStream !== this.stream) {
this.$refs.media.srcObject = this.currentStream; this.loading = true;
} else if(_.isObject(this.currentStream) && _.isObject(this.$refs.media) && this.assignStream(this.stream);
!_.isUndefined(this.$refs.media.mozSrcObject)) { }
this.$refs.media.mozSrcObject = this.currentStream;
} else if(_.isObject(this.currentStream) && _.isObject(this.$refs.media) &&
_.isObject(URL) && _.isFunction(URL.createObjectURL)) {
this.$refs.media.src = URL.createObjectURL(this.currentStream);
} }
}, },
components: {}, computed: {
methods: {}, hasVideo() {
computed: {} return _.isArray(this.currentStream.getVideoTracks()) &&
this.currentStream.getVideoTracks().length > 0;
}
}
} }
</script> </script>
@ -39,4 +70,7 @@
.csc-media video { .csc-media video {
width: 100% width: 100%
} }
.csc-media .csc-spinner {
}
</style> </style>

@ -35,9 +35,6 @@
if(this.right) { if(this.right) {
classes.push('page-title-right'); classes.push('page-title-right');
} }
console.log(classes);
return classes; return classes;
}, },
...mapGetters('layout', ['left', 'right']) ...mapGetters('layout', ['left', 'right'])

@ -53,6 +53,7 @@
import CscCollapsible from '../card/CscCollapsible' import CscCollapsible from '../card/CscCollapsible'
import { QBtn, QCardActions, QCard, QCardSeparator, QInfiniteScroll, import { QBtn, QCardActions, QCard, QCardSeparator, QInfiniteScroll,
QPopover, QList, QItem, QSpinnerDots } from 'quasar-framework' QPopover, QList, QItem, QSpinnerDots } from 'quasar-framework'
import numberFormat from '../../filters/number-format'
export default { export default {
data () { data () {
return { return {
@ -78,8 +79,7 @@
}, },
methods: { methods: {
call(conversation, localMedia) { call(conversation, localMedia) {
let number = conversation.direction == 'out' ? let number = conversation.direction == 'out' ? conversation.callee : conversation.caller;
conversation.callee : conversation.caller;
this.$store.dispatch('call/start', this.$store.dispatch('call/start',
{ number: number, localMedia: localMedia }); { number: number, localMedia: localMedia });
}, },
@ -120,7 +120,11 @@
let direction = item.direction == 'in' ? let direction = item.direction == 'in' ?
this.$t('pages.conversations.labels.from') : this.$t('pages.conversations.labels.from') :
this.$t('pages.conversations.labels.to'); this.$t('pages.conversations.labels.to');
return `${prefix} ${item.type} ${direction} ${item.caller}`; let number = item.caller;
if(item.direction === 'out') {
number = item.callee;
}
return `${prefix} ${item.type} ${direction} ${numberFormat(number)}`;
}, },
isCall(type) { isCall(type) {
return type == 'call'; return type == 'call';

@ -1,7 +1,9 @@
import Vue from 'vue'; import Vue from 'vue';
import NumberFilter from './number' import NumberFilter from './number'
import NumberFormatFilter from './number-format'
import DateFilter from './date' import DateFilter from './date'
Vue.filter('number', NumberFilter); Vue.filter('number', NumberFilter);
Vue.filter('readableDate', DateFilter); Vue.filter('readableDate', DateFilter);
Vue.filter('numberFormat', NumberFormatFilter);

@ -0,0 +1,37 @@
import url from 'url';
import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber';
var phoneUtil = PhoneNumberUtil.getInstance();
export default function(number) {
try {
let phoneNumber = url.parse(number, true).auth.split(':')[0];
return normalizeNumber(phoneNumber);
} catch(err1) {
return normalizeNumber(number);
}
}
export function normalizeNumber(number) {
if(_.isString(number) && number.match(/^\+?[0-9]+$/)) {
let normalizedNumber = number.replace(/\s*/g, '');
if(normalizedNumber.match(/^\+/) === null) {
normalizedNumber = '+' + normalizedNumber;
}
try {
return phoneUtil.format(phoneUtil.parse(normalizedNumber, 'DE'), PhoneNumberFormat.INTERNATIONAL);
} catch(err) {
return normalizedNumber;
}
} else {
return number;
}
}
export function rawNumber(number) {
if(_.isString(number)) {
return number.replace(/\s*/g, '').replace(/^\+/, '');
}
return '';
}

@ -123,6 +123,8 @@
"call": "Call", "call": "Call",
"inputNumber": "Input a phone number", "inputNumber": "Input a phone number",
"inputValidNumber": "Input a valid phone number", "inputValidNumber": "Input a valid phone number",
"number": "Number" "number": "Number",
"endCall": "End Call",
"endCallDialog": "You are about to end the current call. Are you sure?"
} }
} }

@ -1,6 +1,7 @@
import EventEmitter from 'events';
import _ from 'lodash'; import _ from 'lodash';
import { loadCdkLib, connectDefaultCdkNetwork } from '../helpers/cdk-lib'; import { loadCdkLib, connectCdkNetwork } from '../helpers/cdk-lib';
import { createSessionToken } from '../api/rtcsession'; import { createSessionToken } from '../api/rtcsession';
export const LocalMedia = { export const LocalMedia = {
@ -11,26 +12,45 @@ export const LocalMedia = {
screenOnly: 'screenOnly' screenOnly: 'screenOnly'
}; };
export class NetworkNotConnected extends Error { export class NetworkNotConnected {
constructor(network) { constructor(network) {
super(); this.name = "NetworkNotConnected";
this.name = this.constructor.name;
this.message = 'Network ' + network + ' is not connected'; this.message = 'Network ' + network + ' is not connected';
this.network = network; this.network = network;
} }
} }
export class CallNotFound {
constructor() {
this.name = "CallNotFound";
this.message = 'Call not found';
}
}
export class CallAlreadyExists {
constructor() {
this.name = "CallAlreadyExists";
this.message = 'Call already exists';
}
}
var rtcEngineCallInstance = null; var rtcEngineCallInstance = null;
export class RtcEngineCall { export class RtcEngineCall {
constructor(options) { constructor(options) {
this.networkTag = 'sip'; this.networkTag = 'sip';
this.client = null;
this.network = null; this.network = null;
this.currentCall = null;
this.loadedLibrary = null; this.loadedLibrary = null;
this.sessionToken = null; this.sessionToken = null;
this.localCall = null;
this.localMedia = null;
this.remoteCall = null;
this.remoteMedia = null;
this.events = new EventEmitter();
this.endedReason = null;
} }
initialize() { initialize() {
@ -45,6 +65,19 @@ export class RtcEngineCall {
return this.connectNetwork($sessionToken); return this.connectNetwork($sessionToken);
}).then(($network)=>{ }).then(($network)=>{
this.network = $network; this.network = $network;
this.network.onIncomingCall((remoteCall)=>{
if(this.network !== null && this.remoteCall === null) {
this.remoteCall = remoteCall;
this.remoteCall.onEnded(()=>{
this.events.emit('ended', this.remoteCall.endedReason);
}).onRemoteMedia((remoteMediaStream)=>{
this.events.emit('remoteMedia', remoteMediaStream);
}).onRemoteMediaEnded(()=>{
this.events.emit('remoteMediaEnded');
});
}
this.events.emit('incoming');
});
resolve(); resolve();
}).catch((err)=>{ }).catch((err)=>{
reject(err); reject(err);
@ -57,7 +90,7 @@ export class RtcEngineCall {
} }
hasRunningCall() { hasRunningCall() {
return this.currentCall !== null; return this.localCall !== null || this.remoteCall !== null;
} }
loadLibrary() { loadLibrary() {
@ -69,12 +102,12 @@ export class RtcEngineCall {
} }
connectNetwork(session) { connectNetwork(session) {
return connectDefaultCdkNetwork(session); return connectCdkNetwork(session, this.networkTag);
} }
createLocalMedia(localMedia) { createLocalMedia(localMedia) {
return new Promise((resolve, reject)=>{ return new Promise((resolve, reject)=>{
var localMediaStream = new cdk.LocalMediaStream(); this.localMedia = new cdk.LocalMediaStream();
var hasAudio = localMedia === LocalMedia.audioOnly || var hasAudio = localMedia === LocalMedia.audioOnly ||
localMedia === LocalMedia.audioVideo || localMedia === LocalMedia.audioVideo ||
localMedia === LocalMedia.audioScreen; localMedia === LocalMedia.audioScreen;
@ -83,61 +116,137 @@ export class RtcEngineCall {
var hasScreen = localMedia === LocalMedia.audioScreen || var hasScreen = localMedia === LocalMedia.audioScreen ||
localMedia === LocalMedia.screenOnly; localMedia === LocalMedia.screenOnly;
localMediaStream.queryMediaSources((sources) => { this.localMedia.queryMediaSources((sources) => {
if (hasAudio && _.isObject(sources.defaultAudio)) { if (hasAudio && _.isObject(sources.defaultAudio)) {
localMediaStream.setAudio(sources.defaultAudio); this.localMedia.setAudio(sources.defaultAudio);
} }
if (hasVideo && _.isObject(sources.defaultVideo)) { if (hasVideo && _.isObject(sources.defaultVideo)) {
localMediaStream.setVideo(sources.defaultVideo); this.localMedia.setVideo(sources.defaultVideo);
} else if (hasScreen && _.isObject(sources.desktopSharing)) { } else if (hasScreen && _.isObject(sources.desktopSharing)) {
localMediaStream.setVideo(sources.desktopSharing); this.localMedia.setVideo(sources.desktopSharing);
} }
}); });
this.localMedia.build((err)=>{
localMediaStream.build((err)=>{
if(_.isObject(err)) { if(_.isObject(err)) {
reject(err); reject(err);
} else { } else {
resolve(localMediaStream); resolve(this.localMedia);
} }
}); });
}); });
} }
start(peer, localMediaStream) { start(peer, localMediaStream) {
peer = peer.replace('+', ''); if(this.network !== null && this.localCall === null) {
if(this.network !== null) { peer = peer.replace(/(\s|\+)/,'');
this.currentCall = this.network.call(peer, { localMediaStream: localMediaStream }); this.localCall = this.network.call(peer, {
return this.currentCall; localMediaStream: localMediaStream
});
this.localCall.onEnded(()=>{
this.events.emit('ended', this.localCall.endedReason);
this.end();
}).onPending(()=>{
this.events.emit('pending');
}).onRemoteMedia((remoteMediaStream)=>{
this.events.emit('remoteMedia', remoteMediaStream);
}).onRemoteMediaEnded(()=>{
this.events.emit('remoteMediaEnded');
}).onRingingStart(()=>{
this.events.emit('ringingStart');
}).onRingingStop(()=>{
this.events.emit('ringingStop');
});
} else if(this.network !== null) {
throw new CallAlreadyExists();
} else { } else {
throw new NetworkNotConnected(this.networkTag); throw new NetworkNotConnected(this.networkTag);
} }
} }
onIncoming(listener) { getNumber() {
if(this.network !== null) { if(this.localCall !== null) {
this.network.onIncomingCall((call)=>{ return this.localCall.peer;
if(this.currentCall === null) { } else if(this.remoteCall !== null) {
this.currentCall = call; return this.remoteCall.peer;
listener(call);
}
});
} else { } else {
throw new NetworkNotConnected(this.networkTag); return null;
} }
} }
getEndedReason() {
return this.endedReason;
}
fetchEndedReason() {
if(this.localCall !== null) {
return this.localCall.endedReason;
} else if(this.remoteCall !== null) {
return this.remoteCall.endedReason;
} else {
return null;
}
}
onIncoming(listener) {
this.events.on('incoming', listener);
return this;
}
onPending(listener) {
this.events.on('pending', listener);
return this;
}
onRingingStart(listener) {
this.events.on('ringingStart', listener);
return this;
}
onRingingStop(listener) {
this.events.on('ringingStop', listener);
return this;
}
onRemoteMedia(listener) {
this.events.on('remoteMedia', listener);
return this;
}
onRemoteMediaEnded(listener) {
this.events.on('remoteMediaEnded', listener);
return this;
}
onEnded(listener) {
this.events.on('ended', listener);
return this;
}
accept(localMediaStream) { accept(localMediaStream) {
if(this.currentCall !== null) { if(this.remoteCall !== null) {
this.currentCall.accept({ this.remoteCall.accept({
localMediaStream: localMediaStream localMediaStream: localMediaStream
}); });
} }
} }
hangUp() { hangUp() {
if(this.currentCall !== null) { this.end();
this.currentCall.end(); }
end() {
this.endedReason = this.fetchEndedReason();
if(this.localCall !== null) {
this.localCall.end();
this.localCall = null;
}
if(this.remoteCall !== null) {
this.remoteCall.end();
this.remoteCall = null;
}
if(this.localMedia !== null) {
this.localMedia.stop();
this.localMedia = null;
} }
} }

@ -58,6 +58,9 @@ export default {
isInitiating(state, getters) { isInitiating(state, getters) {
return state.callState === CallState.initiating; return state.callState === CallState.initiating;
}, },
isIncoming(state, getters) {
return state.callState === CallState.incoming;
},
isTrying(state, getters) { isTrying(state, getters) {
return state.callState === CallState.initiating || return state.callState === CallState.initiating ||
state.callState === CallState.ringing; state.callState === CallState.ringing;
@ -70,6 +73,9 @@ export default {
state.callState === CallState.ringing || state.callState === CallState.ringing ||
state.callState === CallState.established; state.callState === CallState.established;
}, },
isEstablished(state, getters) {
return state.callState === CallState.established;
},
isEnded(state, getters) { isEnded(state, getters) {
return state.callState === CallState.ended; return state.callState === CallState.ended;
} }
@ -90,21 +96,21 @@ export default {
state.number = options.number; state.number = options.number;
state.mediaType = options.mediaType; state.mediaType = options.mediaType;
state.localMediaType = state.mediaType; state.localMediaType = state.mediaType;
state.localMediaStream = options.localMediaStream;
state.callState = CallState.initiating; state.callState = CallState.initiating;
}, },
acceptIncoming(state, options) { localMediaSuccess(state, localMediaStream) {
state.localMediaStream = options.localMediaStream; state.localMediaStream = localMediaStream;
}, },
startRinging(state) { startRinging(state) {
state.callState = CallState.ringing; state.callState = CallState.ringing;
}, },
establishCall(state, options) { establishCall(state, remoteMediaStream) {
state.remoteMediaStream = options.remoteMediaStream; state.remoteMediaStream = remoteMediaStream;
state.callState = CallState.established; state.callState = CallState.established;
}, },
incomingCall(state, options) { incomingCall(state, options) {
state.callState = CallState.incoming; state.callState = CallState.incoming;
state.number = options.number;
}, },
hangUpCall(state) { hangUpCall(state) {
state.callState = CallState.input; state.callState = CallState.input;
@ -133,20 +139,21 @@ export default {
actions: { actions: {
initialize(context) { initialize(context) {
return new Promise((resolve, reject)=>{ return new Promise((resolve, reject)=>{
Vue.call.onIncoming(()=>{
context.commit('layout/showRight', null, { root: true });
context.commit('incomingCall', {
number: Vue.call.getNumber()
});
}).onRemoteMedia((remoteMediaStream)=>{
context.commit('establishCall', remoteMediaStream);
}).onRemoteMediaEnded(()=>{
context.commit("endRemoteMedia");
}).onEnded(()=>{
Vue.call.end();
context.commit('endCall', Vue.call.getEndedReason());
});
Vue.call.initialize().then(()=>{ Vue.call.initialize().then(()=>{
context.commit('initSucceeded'); context.commit('initSucceeded');
Vue.call.onIncoming((call)=>{
context.commit('incomingCall');
call.onRemoteMedia((remoteMediaStream)=>{
context.commit('establishCall', {
remoteMediaStream: remoteMediaStream
});
}).onRemoteMediaEnded(()=>{
context.commit("endRemoteMedia");
}).onEnded(()=>{
context.commit('endCall', call.endedReason);
});
});
resolve(); resolve();
}).catch((err)=>{ }).catch((err)=>{
context.commit('initFailed', err); context.commit('initFailed', err);
@ -161,54 +168,36 @@ export default {
* @param options.number * @param options.number
*/ */
start(context, options) { start(context, options) {
console.log('start()');
console.log('options.number is', options.number, 'and options.localMedia is', options.localMedia);
context.commit('layout/showRight', null, { root: true }); context.commit('layout/showRight', null, { root: true });
Vue.call.createLocalMedia(options.localMedia).then((localMediaStream)=>{ context.commit('startCalling', {
console.log('Vue.call.createLocalMediai()'); number: options.number,
var call = Vue.call.start(options.number, localMediaStream); mediaType: options.localMedia });
call.onAccepted(()=>{ Promise.resolve().then(()=>{
}).onEnded(()=>{ return Vue.call.createLocalMedia(options.localMedia);
context.commit('endCall', call.endedReason); }).then((localMediaStream)=>{
context.commit('localMediaSuccess', localMediaStream);
}).onPending(()=>{ Vue.call.onRingingStart(()=>{
context.commit('startCalling', { context.commit('startRinging');
number: options.number, }).onRingingStop(()=>{
mediaType: options.localMedia, context.commit('stopRinging');
localMediaStream: localMediaStream }).start(options.number, localMediaStream);
});
}).onRemoteMedia((remoteMediaStream)=>{
context.commit('establishCall', {
remoteMediaStream: remoteMediaStream
});
}).onRemoteMediaEnded(()=>{
context.commit("endRemoteMedia");
}).onRingingStart(()=>{
context.commit('startRinging');
}).onRingingStop(()=>{
context.commit('stopRinging');
});
}).catch((err)=>{ }).catch((err)=>{
context.commit('endCall', err.name); context.commit('endCall', err.name);
Vue.call.end();
}); });
}, },
accept(context, localMedia) { accept(context, localMedia) {
Vue.call.createLocalMedia(localMedia).then((localMediaStream)=>{ Vue.call.createLocalMedia(localMedia).then((localMediaStream)=>{
Vue.call.accept(localMediaStream); Vue.call.accept(localMediaStream);
context.commit('acceptIncoming', { context.commit('localMediaSuccess', localMediaStream);
localMediaStream: localMediaStream
});
}).catch((err)=>{ }).catch((err)=>{
Vue.call.end();
context.commit('endCall', 'localMediaError'); context.commit('endCall', 'localMediaError');
}); });
}, },
hangUp(context) { hangUp(context) {
if(Vue.call.hasRunningCall()) { Vue.call.hangUp();
Vue.call.hangUp(); context.commit('hangUpCall');
context.commit('hangUpCall');
} else {
context.commit('endCall', 'noRunningCall');
}
} }
} }
}; };

Loading…
Cancel
Save