TT#38781 Update conversations list when call ends

What has been done:
- TT#39012, Ensure scroll to top behavior after list is reloaded
- TT#38962, Implement callState watcher that dispatches action when call
  has ended
- TT#39046, Implement dedicated reload action with retry mechanism
- TT#38961, Implement dedicated reload mutation with call item handling
- TT#39150, Fix existing test and create new one

Change-Id: I8316de60be890c9236e5eda13a9acfe59586e7dd
changes/41/22141/6
raxelsen 7 years ago
parent d1071ba5fa
commit 126bd12745

@ -3,37 +3,6 @@ import { saveAs } from 'file-saver'
import Vue from 'vue'
import { getList } from './common'
// export function getConversations(id, page, rows) {
// return new Promise((resolve, reject) => {
// let params = { subscriber_id: id, page: page, rows: rows,
// order_by: 'timestamp', order_by_direction: 'desc' };
// Vue.http.get('api/conversations/', { params: params })
// .then(result => {
// let jsonBody = getJsonBody(result.body);
// if (_.has(jsonBody, "_embedded.ngcp:conversations")) {
// let list = [];
// _.forEach(jsonBody._embedded['ngcp:conversations'], function(item) {
// let inputString = `${item.type}${item.call_type}${item.id}`;
// let id = crypto.createHash('sha256').update(inputString).digest('base64');
// item._id = id;
// delete item._links;
// if (item.type == 'call') {
// item.type = item.call_type != 'call' ? 'callforward'
// : item.type;
// }
// list.push(item);
// });
// resolve(list);
// }
// else {
// reject(new Error('No items returned for this page.'))
// }
// }).catch((err) => {
// reject(err);
// });
// });
// }
export function getConversations(id, page, rows) {
return getList({
path: 'api/conversations/',

@ -36,7 +36,6 @@
<script>
import {
mapState,
mapGetters
} from 'vuex'
import CscPage from '../../CscPage'
@ -51,8 +50,10 @@
QScrollObservable,
scroll,
QList,
QSpinnerDots
QSpinnerDots,
dom
} from 'quasar-framework'
const { offset } = dom
export default {
data () {
return {
@ -70,15 +71,18 @@
this.$store.commit('conversations/resetList');
},
computed: {
...mapState('conversations', [
...mapGetters('conversations', [
'items',
'isNextPageRequesting',
'downloadFaxState',
'downloadVoiceMailState',
'downloadFaxError',
'downloadVoiceMailError'
'downloadVoiceMailError',
'itemsReloaded',
'reloadItemsError'
]),
...mapGetters('conversations', [
'items',
'isNextPageRequesting'
...mapGetters('call', [
'callState'
])
},
methods: {
@ -112,6 +116,9 @@
id: voiceMail.id,
format: voiceMail.format
});
},
reloadItems() {
this.$store.dispatch('conversations/reloadItems', 1);
}
},
watch: {
@ -140,6 +147,26 @@
stopLoading();
showToast(this.$t('pages.conversations.downloadFaxSuccessMessage'));
}
},
reloadItemsState(state) {
if (state === 'failed') {
showGlobalError(this.reloadItemsError);
}
},
callState(newState, oldState) {
let endedA = newState === 'ended';
let endedB = oldState === 'established' && newState === 'input';
let endedC = oldState === 'ringing' && newState === 'input';
let endedD = oldState === 'incoming' && newState === 'input';
if (endedA || endedB || endedC || endedD) {
this.reloadItems();
}
},
itemsReloaded(state) {
let offsetTop = offset(this.$el).top;
if (state && offsetTop < -15) {
window.scrollTo(0, 0);
}
}
}
}

@ -118,7 +118,8 @@
"downloadVoiceMailSuccessMessage": "Voicemail downloaded successfully",
"downloadVoiceMailErrorMessage": "Downloading of voicemail failed",
"downloadFaxSuccessMessage": "Fax downloaded successfully",
"downloadFaxErrorMessage": "Downloading of fax failed"
"downloadFaxErrorMessage": "Downloading of fax failed",
"reloadItemsErrorMessage": "Reloading conversation items failed"
},
"reminder": {
"title": "Reminder",

@ -236,8 +236,6 @@ export default {
});
}).onRemoteMedia((remoteMediaStream)=>{
context.commit('establishCall', remoteMediaStream);
}).onRemoteMediaEnded(()=>{
context.commit("endRemoteMedia");
}).onEnded(()=>{
Vue.call.end();
context.commit('endCall', Vue.call.getEndedReason());

@ -1,6 +1,7 @@
'use strict';
import Vue from 'vue'
import Vue from 'vue';
import _ from 'lodash';
import { i18n } from '../i18n';
import {
getConversations,
@ -18,6 +19,29 @@ const RequestState = {
failed: 'failed'
};
const ReloadConfig = {
retryLimit: 5,
retryDelay: 5000
};
function linkCallsWithSameId(state) {
let callId = null;
let callIndex = null;
state.items.forEach((item, index)=>{
if(item.type === 'call' && item.call_type === 'call') {
callId = item.call_id;
callIndex = index;
}
else if (item.type === 'call' && item.call_id === callId) {
let temp = state.items[callIndex];
item.relatedCall = temp;
state.items[callIndex] = item;
state.items[index] = temp;
callIndex = index;
}
});
}
export default {
namespaced: true,
state: {
@ -28,8 +52,8 @@ export default {
downloadVoiceMailError: null,
downloadFaxState: RequestState.button,
downloadFaxError: null,
reloadConversationsState: RequestState.button,
reloadConversationsError: null,
reloadItemsState: RequestState.button,
reloadItemsError: null,
playVoiceMailUrls: {},
playVoiceMailStates: {},
playVoiceMailErrors: {},
@ -37,18 +61,19 @@ export default {
lastPage: null,
nextPageState: RequestState.initiated,
nextPageError: null,
items: []
items: [],
itemsReloaded: false
},
getters: {
getSubscriberId(state, getters, rootState, rootGetters) {
return rootGetters['user/getSubscriberId'];
},
reloadConversationsState(state) {
return state.reloadConversationsState;
reloadItemsState(state) {
return state.reloadItemsState;
},
reloadConversationsError(state) {
return state.reloadConversationsError ||
i18n.t('pages.conversations.reloadConversationsErrorMessage');
reloadItemsError(state) {
return state.reloadItemsError ||
i18n.t('pages.conversations.reloadItemsErrorMessage');
},
playVoiceMailState(state) {
return (id) => {
@ -74,13 +99,24 @@ export default {
},
isNextPageRequesting(state) {
return state.nextPageState === RequestState.requesting;
},
downloadFaxState(state) {
return state.downloadFaxState;
},
downloadVoiceMailState(state) {
return state.downloadVoiceMailState;
},
downloadFaxError(state) {
return state.downloadFaxError;
},
downloadVoiceMailError(state) {
return state.downloadVoiceMailError;
},
itemsReloaded(state) {
return state.itemsReloaded;
}
},
mutations: {
loadConversations(state, options) {
state.conversations = state.conversations.concat(options);
state.page++;
},
downloadVoiceMailRequesting(state) {
state.downloadVoiceMailState = RequestState.requesting;
state.downloadVoiceMailError = null;
@ -105,24 +141,21 @@ export default {
state.downloadFaxState = RequestState.failed;
state.downloadFaxError = error;
},
reloadConversationsRequesting(state) {
state.reloadConversationsState = RequestState.requesting;
state.reloadConversationsError = null;
},
reloadConversationsSucceeded(state) {
state.reloadConversationsState = RequestState.succeeded;
state.reloadConversationsError = null;
reloadItemsRequesting(state) {
state.reloadItemsState = RequestState.requesting;
state.reloadItemsError = null;
state.itemsReloaded = false;
},
reloadConversationsFailed(state, error) {
state.reloadConversationsState = RequestState.failed;
state.reloadConversationsError = error;
reloadItemsSucceeded(state, items) {
state.reloadItemsState = RequestState.succeeded;
state.reloadItemsError = null;
state.items = items.items;
linkCallsWithSameId(state);
state.itemsReloaded = true;
},
resetConversations(state) {
state.page = 1;
},
reloadConversations(state, result) {
state.conversations = result;
state.page++;
reloadItemsFailed(state, error) {
state.reloadItemsState = RequestState.failed;
state.reloadItemsError = error;
},
playVoiceMailRequesting(state, id) {
Vue.set(state.playVoiceMailStates, id, RequestState.requesting);
@ -153,23 +186,7 @@ export default {
state.items = state.items.concat(items.items);
state.lastPage = items.lastPage;
state.currentPage = state.currentPage + 1;
let callId = null;
let callIndex = null;
state.items.forEach((item, index)=>{
if(item.type === 'call' && item.call_type === 'call') {
callId = item.call_id;
callIndex = index;
}
else if (item.type === 'call' && item.call_id === callId) {
let temp = state.items[callIndex];
item.relatedCall = temp;
state.items[callIndex] = item;
state.items[index] = temp;
callIndex = index;
}
});
linkCallsWithSameId(state);
},
nextPageFailed(state, error) {
state.nextPageState = RequestState.failed;
@ -177,7 +194,32 @@ export default {
}
},
actions: {
reloadItems(context, retryCount) {
context.commit('reloadItemsRequesting');
let rows = context.state.currentPage * ROWS_PER_PAGE;
let firstStateItemTimestamp = context.state.items[0] ?
context.state.items[0].start_time : null;
if (retryCount < ReloadConfig.retryLimit) {
getConversations(
context.getters.getSubscriberId,
1,
rows
).then((result) => {
let firstResultItemTimestamp = result.items[0] ?
result.items[0].start_time : null;
if (_.isEqual(firstStateItemTimestamp, firstResultItemTimestamp)) {
setTimeout(() => {
context.dispatch('reloadItems', ++retryCount);
}, ReloadConfig.retryDelay);
}
else {
context.commit('reloadItemsSucceeded', result);
}
}).catch((err) => {
context.commit('reloadItemsFailed', err.message);
});
}
},
downloadVoiceMail(context, id) {
context.commit('downloadVoiceMailRequesting');
downloadVoiceMail(id).then(()=>{

@ -6,23 +6,125 @@ import { assert } from 'chai';
describe('Conversations', function(){
it('should load conversations', function(){
it('should load next page of items', function(){
let resultItems = [];
let state = {
conversations: [
items: [
{
call_id: "8fe2fa2f-84bc-48be-977d-84984aa5cc29",
call_type: "call",
callee: "43993006",
caller: "43993004",
currency: "",
customer_cost: 0,
direction: "out",
duration: "0:00:00",
id: 85,
rating_status: "ok",
start_time: "2018-06-21 14:50:00.687",
status: "noanswer",
total_customer_cost: 0,
type: "call",
_links: {
}
}
]
};
let data = [
{
"caller": "43993010",
"type": "call"
},
{
"caller": "43993011",
"type": "fax"
}
];
ConversationsModule.mutations.loadConversations(state, data);
assert.deepEqual(state.conversations, data);
let data = {
items: [
{
call_id: "8fe2fa2f-84bc-48be-977d-84984aa5cc29",
call_type: "call",
callee: "43993006",
caller: "43993004",
currency: "",
customer_cost: 0,
direction: "out",
duration: "0:00:00",
id: 85,
rating_status: "ok",
start_time: "2018-06-21 14:50:00.687",
status: "noanswer",
total_customer_cost: 0,
type: "call",
_links: {
}
}
],
lastPage: 1
};
resultItems.push(state.items[0]);
resultItems.push(data.items[0]);
ConversationsModule.mutations.nextPageSucceeded(state, data);
assert.deepEqual(state.items, resultItems);
});
it('should load reloaded items', function(){
let state = {
items: [
{
call_id: "8fe2fa2f-84bc-48be-977d-84984aa5cc29",
call_type: "call",
callee: "43993006",
caller: "43993004",
currency: "",
customer_cost: 0,
direction: "out",
duration: "0:00:00",
id: 85,
rating_status: "ok",
start_time: "2018-06-21 14:50:00.687",
status: "noanswer",
total_customer_cost: 0,
type: "call",
_links: {
}
}
]
};
let data = {
items: [
{
call_id: "d2212956-46cc-4f9d-805d-cf2b5f572726",
call_type: "call",
callee: "43993007",
caller: "43993004",
currency: "",
customer_cost: 0,
direction: "out",
duration: "0:00:00",
id: 87,
rating_status: "ok",
start_time: "2018-06-21 15:02:41.762",
status: "noanswer",
total_customer_cost: 0,
type: "call",
_links: {
}
},
{
call_id: "8fe2fa2f-84bc-48be-977d-84984aa5cc29",
call_type: "call",
callee: "43993006",
caller: "43993004",
currency: "",
customer_cost: 0,
direction: "out",
duration: "0:00:00",
id: 85,
rating_status: "ok",
start_time: "2018-06-21 14:50:00.687",
status: "noanswer",
total_customer_cost: 0,
type: "call",
_links: {
}
}
],
lastPage: 1
};
ConversationsModule.mutations.reloadItemsSucceeded(state, data);
assert.deepEqual(state.items, data.items);
});
});

Loading…
Cancel
Save