TT#45671 Call: Improved error handling

Change-Id: Ia337ed761c9e58ebbafb0e73e03b918d14508a47
changes/46/24646/1
Hans-Peter Herzog 7 years ago
parent 5998565369
commit 36330a23f6

@ -21,8 +21,18 @@
</form> </form>
</q-card-main> </q-card-main>
<q-card-actions class="pull-right"> <q-card-actions class="pull-right">
<q-btn flat icon="fa-arrow-right" <q-spinner-dots
color="primary" @click="login()">{{ $t('pages.login.button') }}</q-btn> v-if="loginRequesting"
color="primary"
size="32px"
/>
<q-btn
v-if="!loginRequesting"
flat
icon="fa-arrow-right"
color="primary"
@click="login()"
>{{ $t('pages.login.button') }}</q-btn>
</q-card-actions> </q-card-actions>
</q-card> </q-card>
</div> </div>
@ -33,10 +43,28 @@
<script> <script>
import { mapGetters } from 'vuex' import {
import { startLoading, stopLoading, showGlobalError } from '../helpers/ui' mapGetters
import { QLayout, QCard, QCardTitle, QCardSeparator, QCardMain, QField, QInput, } from 'vuex'
QCardActions, QBtn, QIcon, Platform } from 'quasar-framework' import {
// startLoading,
// stopLoading,
showGlobalError
} from '../helpers/ui'
import {
QLayout,
QCard,
QCardTitle,
QCardSeparator,
QCardMain,
QField,
QInput,
QCardActions,
QBtn,
QIcon,
Platform,
QSpinnerDots
} from 'quasar-framework'
export default { export default {
name: 'login', name: 'login',
@ -50,7 +78,8 @@
QInput, QInput,
QCardActions, QCardActions,
QBtn, QBtn,
QIcon QIcon,
QSpinnerDots
}, },
data () { data () {
return { return {
@ -84,11 +113,11 @@
} }
}, },
watch: { watch: {
loginRequesting(logging) { // loginRequesting(logging) {
if(logging) { // if(logging) {
startLoading(); // startLoading();
} // }
}, // },
loginSucceeded(loggedIn) { loginSucceeded(loggedIn) {
if(loggedIn) { if(loggedIn) {
this.$router.push({path : '/'}); this.$router.push({path : '/'});
@ -96,7 +125,6 @@
}, },
loginError(error) { loginError(error) {
if(error) { if(error) {
stopLoading();
showGlobalError(this.$i18n.t('pages.login.error')); showGlobalError(this.$i18n.t('pages.login.error'));
} }
} }

@ -671,6 +671,7 @@
.csc-call-content .csc-call-content
background-color transparent background-color transparent
.csc-call.csc-call-minimized .csc-call.csc-call-minimized
opacity 0
height $call-footer-height-big height $call-footer-height-big
top auto top auto
bottom ($call-footer-height-big + $call-footer-action-margin) * -1 bottom ($call-footer-height-big + $call-footer-action-margin) * -1

@ -149,7 +149,7 @@
</q-collapsible> </q-collapsible>
<q-collapsible <q-collapsible
:opened="isCallBlocking" :opened="isCallBlocking"
intend icon="fa-ban" intend icon="block"
:label="$t('navigation.callBlocking.title')" :label="$t('navigation.callBlocking.title')"
:sublabel="$t('navigation.callBlocking.subTitle')" :sublabel="$t('navigation.callBlocking.subTitle')"
> >
@ -312,9 +312,6 @@
mobileMenu: null mobileMenu: null
} }
}, },
mounted: function() {
this.$store.dispatch('user/initUser');
},
mixins: [ mixins: [
platformMixin platformMixin
], ],
@ -499,14 +496,8 @@
this.setCallStateTitle(); this.setCallStateTitle();
} }
}, },
userDataRequesting(value) { userDataSucceeded(userDataSucceeded) {
if(value) { if(userDataSucceeded) {
startLoading();
}
},
userDataSucceeded(value) {
if(value) {
stopLoading();
enableIncomingCallNotifications(); enableIncomingCallNotifications();
} }
}, },

@ -5,7 +5,7 @@
<q-item tag="label"> <q-item tag="label">
<q-item-side> <q-item-side>
<q-radio v-model="enabled" val="blacklist" color="negative" <q-radio v-model="enabled" val="blacklist" color="negative"
checked-icon="fa-ban" uncheck-icon="fa-ban"/> checked-icon="block" uncheck-icon="block"/>
</q-item-side> </q-item-side>
<q-item-main> <q-item-main>
<q-item-tile label>{{ $t('pages.callBlocking' + suffix + '.toggleDisableLabel') }}</q-item-tile> <q-item-tile label>{{ $t('pages.callBlocking' + suffix + '.toggleDisableLabel') }}</q-item-tile>
@ -39,7 +39,7 @@
<div> <div>
<q-card class="blocked-number" v-for="(number, index) in numbers" :key="index"> <q-card class="blocked-number" v-for="(number, index) in numbers" :key="index">
<q-card-title> <q-card-title>
<q-icon v-if="!(editing && editingIndex == index) && enabled == 'blacklist'" name="fa-ban" color="negative" size="22px"/> <q-icon v-if="!(editing && editingIndex == index) && enabled == 'blacklist'" name="block" color="negative" size="22px"/>
<q-icon v-if="!(editing && editingIndex == index) && enabled == 'whitelist'" name="check" color="primary" size="22px"/> <q-icon v-if="!(editing && editingIndex == index) && enabled == 'whitelist'" name="check" color="primary" size="22px"/>
<span class="blocked-number-title" v-if="!(editing && editingIndex == index)" <span class="blocked-number-title" v-if="!(editing && editingIndex == index)"
@click="editNumber(index)">{{ number }}</span> @click="editNumber(index)">{{ number }}</span>

@ -9,7 +9,7 @@
class="csc-call-page-content" class="csc-call-page-content"
> >
<q-alert <q-alert
v-if="desktopSharingInstall" v-if="!isCallInitializing && desktopSharingInstall"
v-model="desktopSharingInstall" v-model="desktopSharingInstall"
color="warning" color="warning"
:actions="desktopSharingAlertActions" :actions="desktopSharingAlertActions"
@ -17,20 +17,30 @@
{{ $t('call.desktopSharingNotInstalled') }} {{ $t('call.desktopSharingNotInstalled') }}
</q-alert> </q-alert>
<q-alert <q-alert
v-if="!isCallInitializing && !hasRtcEngineCapabilityEnabled"
class="csc-inline-alert" class="csc-inline-alert"
appear appear
icon="info" icon="info"
color="info" color="info"
v-if="!hasRtcEngineCapabilityEnabled"
:actions="rtcEngineInfoActions" :actions="rtcEngineInfoActions"
> >
{{ $t('call.rtcEngineNotEnabled') }} {{ $t('call.rtcEngineNotEnabled') }}
</q-alert> </q-alert>
<div
v-if="isCallInitializing"
class="csc-main-spinner"
>
<q-spinner-dots
size="32px"
color="primary"
/>
</div>
<csc-phone-number-input <csc-phone-number-input
v-if="!isCallInitializing"
class="csc-call-phone-number" class="csc-call-phone-number"
:dark="false" :dark="false"
:value="callNumberInput" :value="callNumberInput"
:enabled="hasRtcEngineCapabilityEnabled" :enabled="isCallInitialized"
@number-changed="numberInputChanged" @number-changed="numberInputChanged"
/> />
</div> </div>
@ -49,7 +59,8 @@
import CscPhoneNumberInput from "../call/CscPhoneNumberInput"; import CscPhoneNumberInput from "../call/CscPhoneNumberInput";
import { import {
QIcon, QIcon,
QAlert QAlert,
QSpinnerDots
} from 'quasar-framework' } from 'quasar-framework'
export default { export default {
data() { data() {
@ -60,7 +71,8 @@
CscPhoneNumberInput, CscPhoneNumberInput,
CscPage, CscPage,
QIcon, QIcon,
QAlert QAlert,
QSpinnerDots
}, },
methods: { methods: {
numberInputChanged(number) { numberInputChanged(number) {
@ -72,7 +84,9 @@
'callNumberInput', 'callNumberInput',
'hasCallInitError', 'hasCallInitError',
'hasRtcEngineCapabilityEnabled', 'hasRtcEngineCapabilityEnabled',
'desktopSharingInstall' 'desktopSharingInstall',
'isCallInitialized',
'isCallInitializing'
]), ]),
rtcEngineInfoActions() { rtcEngineInfoActions() {
return []; return [];

@ -11,6 +11,9 @@ import {
import { import {
startCase startCase
} from '../filters/string' } from '../filters/string'
import {
RequestState
} from './common'
export var CallState = { export var CallState = {
input: 'input', input: 'input',
@ -27,12 +30,32 @@ export var MediaType = {
audioScreen: 'audioScreen' audioScreen: 'audioScreen'
}; };
export const errorVisibilityTimeout = 5000;
export const reinitializeTimeout = 5000;
function handleUserMediaError(context, err) {
let errName = _.get(err, 'name', null);
let errMessage = _.get(err, 'message', null);
if(errName === 'UserMediaError' && errMessage === 'Permission denied') {
context.commit('endCall', 'userMediaPermissionDenied');
}
if(errMessage === 'plugin not detected') {
context.commit('desktopSharingInstall');
context.commit('hangUpCall');
}
else if(errMessage === 'PermissionDenied') {
context.commit('endCall', 'desktopSharingPermissionDenied');
}
else {
context.commit('endCall', errName);
}
}
export default { export default {
namespaced: true, namespaced: true,
state: { state: {
initialized: false, initializationState: RequestState.initiated,
initError: null, initializationError: null,
disabled: false,
endedReason: null, endedReason: null,
callState: CallState.input, callState: CallState.input,
number: '', number: '',
@ -64,11 +87,18 @@ export default {
isCallAvailable(state, getters) { isCallAvailable(state, getters) {
return getters.isNetworkConnected; return getters.isNetworkConnected;
}, },
isCallInitializing(state, getters, rootState, rootGetters) {
return state.initializationState === RequestState.requesting ||
rootGetters['user/userDataRequesting'];
},
isCallInitialized(state) {
return state.initializationState === RequestState.succeeded
},
hasCallInitError(state) { hasCallInitError(state) {
return state.initError !== null && state.disabled === false; return state.initializationError !== null;
}, },
callInitError(state) { callInitError(state) {
return state.initError; return state.initializationError;
}, },
isPreparing(state) { isPreparing(state) {
return state.callState === CallState.input; return state.callState === CallState.input;
@ -184,19 +214,18 @@ export default {
} }
}, },
mutations: { mutations: {
numberInputChanged(state, numberInput) { initRequesting(state) {
state.numberInput = numberInput; state.initializationState = RequestState.requesting;
}, },
initSucceeded(state) { initSucceeded(state) {
state.initialized = true; state.initializationState = RequestState.succeeded;
state.initError = null;
}, },
initFailed(state, err) { initFailed(state, error) {
state.initialized = false; state.initializationState = RequestState.failed;
state.initError = err; state.initializationError = error;
}, },
disable(state) { numberInputChanged(state, numberInput) {
state.disabled = true; state.numberInput = numberInput;
}, },
inputNumber(state) { inputNumber(state) {
state.callState = CallState.input; state.callState = CallState.input;
@ -294,7 +323,8 @@ export default {
}, },
actions: { actions: {
initialize(context) { initialize(context) {
return new Promise((resolve, reject)=>{ if(!context.getters.isCallInitialized) {
context.commit('initRequesting');
Vue.call.onIncoming(()=>{ Vue.call.onIncoming(()=>{
context.commit('incomingCall', { context.commit('incomingCall', {
number: Vue.call.getNumber() number: Vue.call.getNumber()
@ -304,21 +334,16 @@ export default {
}).onEnded(()=>{ }).onEnded(()=>{
Vue.call.end(); Vue.call.end();
context.commit('endCall', Vue.call.getEndedReason()); context.commit('endCall', Vue.call.getEndedReason());
setTimeout(()=>{
context.commit('inputNumber');
}, errorVisibilityTimeout);
}); });
if(context.getters.hasRtcEngineCapabilityEnabled) { Vue.call.initialize().then(()=>{
Vue.call.initialize().then(()=>{ context.commit('initSucceeded');
context.commit('initSucceeded'); }).catch((err)=>{
resolve(); context.commit('initFailed', err);
}).catch((err)=>{ });
context.commit('initFailed', err); }
reject(err);
});
}
else {
context.commit('disable');
resolve();
}
});
}, },
start(context, localMedia) { start(context, localMedia) {
let number = context.getters.callNumberInput; let number = context.getters.callNumberInput;
@ -335,13 +360,10 @@ export default {
}).start(number, localMediaStream); }).start(number, localMediaStream);
}).catch((err)=>{ }).catch((err)=>{
Vue.call.end(); Vue.call.end();
if(err.message === 'plugin not detected') { handleUserMediaError(context, err);
context.commit('desktopSharingInstall'); setTimeout(()=>{
context.commit('hangUpCall'); context.commit('inputNumber');
} }, errorVisibilityTimeout);
else {
context.commit('endCall', err.name);
}
}); });
}, },
accept(context, localMedia) { accept(context, localMedia) {
@ -351,13 +373,10 @@ export default {
context.commit('localMediaSuccess', localMediaStream); context.commit('localMediaSuccess', localMediaStream);
}).catch((err)=>{ }).catch((err)=>{
Vue.call.end(); Vue.call.end();
if(err.message === 'plugin not detected') { handleUserMediaError(context, err);
context.commit('desktopSharingInstall'); setTimeout(()=>{
context.commit('hangUpCall'); context.commit('inputNumber');
} }, errorVisibilityTimeout);
else {
context.commit('endCall', err.name);
}
}); });
}, },
end(context) { end(context) {

@ -1,7 +1,10 @@
'use strict'; 'use strict';
import _ from 'lodash'; import _ from 'lodash';
import { login, getUserData} from '../api/user'; import {
login,
getUserData
} from '../api/user';
export default { export default {
namespaced: true, namespaced: true,
@ -69,11 +72,11 @@ export default {
getSubscriberId(state) { getSubscriberId(state) {
return state.subscriberId; return state.subscriberId;
}, },
loginRequesting(state) { loginRequesting(state, getters) {
return state.loginRequesting; return state.loginRequesting || getters.userDataRequesting;
}, },
loginSucceeded(state) { loginSucceeded(state, getters) {
return state.loginSucceeded; return state.loginSucceeded && getters.userDataSucceeded;
}, },
loginError(state) { loginError(state) {
return state.loginError; return state.loginError;
@ -163,6 +166,7 @@ export default {
jwt: localStorage.getItem('jwt'), jwt: localStorage.getItem('jwt'),
subscriberId: localStorage.getItem('subscriberId') subscriberId: localStorage.getItem('subscriberId')
}); });
context.dispatch('initUser');
}).catch((err)=>{ }).catch((err)=>{
context.commit('loginFailed', err.message); context.commit('loginFailed', err.message);
}); });
@ -188,7 +192,9 @@ export default {
context.dispatch('logout'); context.dispatch('logout');
}, context.getters.jwtTTL * 1000); }, context.getters.jwtTTL * 1000);
} }
context.dispatch('call/initialize', null, { root: true }); if(context.getters.hasRtcEngineCapabilityEnabled) {
context.dispatch('call/initialize', null, { root: true });
}
}).catch((err)=>{ }).catch((err)=>{
context.commit('userDataFailed', err.message); context.commit('userDataFailed', err.message);
context.dispatch('logout'); context.dispatch('logout');

@ -1,6 +1,9 @@
@import 'quasar.variables' @import 'quasar.variables'
.csc-main-spinner
text-align center
.csc-inline-alert .csc-inline-alert
box-shadow none box-shadow none
.q-alert .q-alert

Loading…
Cancel
Save