TT#51303 API method to fetch blocked numbers

What has been done:
  - TT#51303, Create API method for fetching call blocking lists
  - TT#51304, Implement state handling and getter for call blocking list
  fetching
  - TT#51356, Create API update methods for call blocking
  - TT#51354, Implement UI menu items, success toasts and error handling
  - TT#51355, Implement state handling and actions for call blocking update
  requests

Change-Id: I468d1152d17a33fbb56bc4f052beb0440a3fb7b3
changes/41/26841/10
raxelsen 7 years ago
parent d5bc3d4552
commit 1f051f4ba0

@ -1,5 +1,6 @@
import _ from 'lodash';
import Vue from 'vue';
import {
enableBlockIn,
@ -147,3 +148,76 @@ export function getPrivacyCallBlocking(id) {
});
});
}
export function removeNumberFromList(id, field, value) {
return new Promise((resolve, reject)=>{
Promise.resolve().then(()=>{
return getPreferences(id);
}).then((result)=>{
var prefs = _.cloneDeep(result);
delete prefs._links;
prefs[field] = _.get(prefs, field, []).filter((number) => {
return number !== value;
});
return Vue.http.put('api/subscriberpreferences/' + id, prefs);
}).then(()=>{
resolve();
}).catch((err)=>{
reject(err);
});
});
}
export function removeFromIncomingListByNumber(id, number) {
return new Promise((resolve, reject) => {
removeNumberFromList(id, 'block_in_list', number).then(() => {
resolve()
}).catch((err) => {
reject(err);
});
});
}
export function removeFromOutgoingListByNumber(id, number) {
return new Promise((resolve, reject) => {
removeNumberFromList(id, 'block_out_list', number).then(() => {
resolve()
}).catch((err) => {
reject(err);
});
});
}
export function toggleNumberInBothLists(options) {
return new Promise((resolve, reject) => {
Promise.resolve().then(() => {
return getPreferences(options.id);
}).then((result) => {
let prefs = _.cloneDeep(result);
delete prefs._links;
prefs['block_in_list'] = _.get(prefs, 'block_in_list', []);
prefs['block_out_list'] = _.get(prefs, 'block_out_list', []);
if (options.block_in_list === 'add') {
prefs['block_in_list'] = [options.number].concat(prefs['block_in_list']);
}
else if (options.block_in_list === 'remove') {
prefs['block_in_list'] = prefs['block_in_list'].filter((number) => {
return number !== options.number;
});
}
if (options.block_out_list === 'add') {
prefs['block_out_list'] = [options.number].concat(prefs['block_out_list']);
}
else if (options.block_out_list === 'remove') {
prefs['block_out_list'] = prefs['block_out_list'].filter((number) => {
return number !== options.number;
});
}
return Vue.http.put('api/subscriberpreferences/' + options.id, prefs);
}).then(() => {
resolve();
}).catch((err) => {
reject(err);
});
});
}

@ -2,10 +2,14 @@
import _ from 'lodash'
import { saveAs } from 'file-saver'
import Vue from 'vue'
import {
getIncomingCallBlocking,
getOutgoingCallBlocking
} from './call-blocking'
import { getList } from './common'
export function getConversations(options) {
return new Promise((resolve, reject)=>{
return new Promise((resolve, reject) => {
let type = _.get(options, 'type', null);
let params ={
subscriber_id: _.get(options, 'subscriberId'),
@ -32,7 +36,7 @@ export function getConversations(options) {
}
export function downloadVoiceMail(id) {
return new Promise((resolve, reject)=>{
return new Promise((resolve, reject) => {
Vue.http.get('api/voicemailrecordings/' + id, { responseType: 'blob' })
.then((res) => {
return res.blob();
@ -46,7 +50,7 @@ export function downloadVoiceMail(id) {
}
export function downloadFax(id) {
return new Promise((resolve, reject)=>{
return new Promise((resolve, reject) => {
Vue.http.get('api/faxrecordings/' + id, { responseType: 'blob' })
.then((res) => {
return res.blob();
@ -60,7 +64,7 @@ export function downloadFax(id) {
}
export function playVoiceMail(options) {
return new Promise((resolve, reject)=>{
return new Promise((resolve, reject) => {
let params = { format: options.format };
Vue.http.get(`api/voicemailrecordings/${options.id}`, { params: params, responseType: 'blob' })
.then((res) => {
@ -70,3 +74,24 @@ export function playVoiceMail(options) {
});
});
}
export function getIncomingBlocked(id) {
return new Promise((resolve, reject) => {
getIncomingCallBlocking(id).then((list) => {
resolve(list)
}).catch((err) => {
reject(err);
});
});
}
export function getOutgoingBlocked(id) {
return new Promise((resolve, reject) => {
getOutgoingCallBlocking(id).then((list) => {
resolve(list)
}).catch((err) => {
reject(err);
});
});
}

@ -56,10 +56,15 @@
:key="item._id"
:item="item"
:call-available="isCallAvailable"
:blocked-incoming="blockedIncoming(item)"
:blocked-outgoing="blockedOutgoing(item)"
@start-call="startCall"
@download-fax="downloadFax"
@download-voice-mail="downloadVoiceMail"
@play-voice-mail="playVoiceMail"
@toggle-block-incoming="toggleBlockIncoming"
@toggle-block-outgoing="toggleBlockOutgoing"
@toggle-block-both="toggleBlockBoth"
/>
</q-list>
<div
@ -164,6 +169,7 @@
},
created() {
this.$store.commit('conversations/resetList');
this.$store.dispatch('conversations/getBlockedNumbers');
},
computed: {
...mapGetters('conversations', [
@ -174,7 +180,11 @@
'downloadFaxError',
'downloadVoiceMailError',
'itemsReloaded',
'reloadItemsError'
'reloadItemsError',
'toggleBlockedState',
'lastToggledType',
'isNumberIncomingBlocked',
'isNumberOutgoingBlocked'
]),
...mapGetters('call', [
'callState',
@ -296,6 +306,32 @@
this.$store.commit('conversations/resetList');
this.$store.dispatch('conversations/nextPage', type);
}
},
toggleBlockIncoming(options) {
this.$store.dispatch('conversations/toggleBlockIncoming', options);
},
toggleBlockOutgoing(options) {
this.$store.dispatch('conversations/toggleBlockOutgoing', options);
},
toggleBlockBoth(options) {
this.$store.dispatch('conversations/toggleBlockBoth', options);
},
blockedIncoming(item) {
if (item.direction === 'out') {
return this.isNumberIncomingBlocked(item.callee);
}
else {
return this.isNumberIncomingBlocked(item.caller);
}
},
blockedOutgoing(item) {
if (item.direction === 'out') {
return this.isNumberOutgoingBlocked(item.callee);
}
else {
return this.isNumberOutgoingBlocked(item.caller);
}
}
},
watch: {
@ -347,6 +383,20 @@
if (state && offsetTop < -15) {
window.scrollTo(0, 0);
}
},
toggleBlockedState(state) {
if (state === 'requesting') {
startLoading();
}
else if (state === 'failed') {
stopLoading();
}
else if (state === 'succeeded') {
stopLoading();
showToast(this.$t('pages.conversations.toggledSuccessMessage', {
type: this.lastToggledType
}));
}
}
}
}

@ -77,6 +77,40 @@
:label="$t('pages.conversations.buttons.call')"
/>
</q-item>
<q-item
@click="toggleBlockIncoming"
>
<q-item-side
icon="call received"
color="primary"
/>
<q-item-main
:label="blockIncomingLabel"
/>
</q-item>
<q-item
@click="toggleBlockOutgoing"
>
<q-item-side
icon="call made"
color="primary"
/>
<q-item-main
:label="blockOutgoingLabel"
/>
</q-item>
<q-item
v-if="blockBothPossible"
@click="toggleBlockBoth"
>
<q-item-side
icon="block"
color="primary"
/>
<q-item-main
:label="blockBothLabel"
/>
</q-item>
</q-list>
</q-popover>
</q-btn>
@ -102,7 +136,11 @@
name: 'csc-call-item',
props: [
'call',
'callAvailable'
'callAvailable',
'blockIncomingLabel',
'blockOutgoingLabel',
'blockBothLabel',
'blockBothPossible'
],
components: {
QList,
@ -192,6 +230,15 @@
startCall() {
this.$refs.callPopover.close();
this.$emit('start-call', this.numberDialBack);
},
toggleBlockIncoming() {
this.$emit('toggle-block-incoming');
},
toggleBlockOutgoing() {
this.$emit('toggle-block-outgoing');
},
toggleBlockBoth() {
this.$emit('toggle-block-both');
}
}
}

@ -3,7 +3,14 @@
v-if="item.type == 'call'"
:call="item"
:call-available="callAvailable"
:block-incoming-label="blockIncomingLabel"
:block-outgoing-label="blockOutgoingLabel"
:block-both-label="blockBothLabel"
:block-both-possible="unblockedBoth || blockedBoth"
@start-call="startCall"
@toggle-block-incoming="toggleBlockIncoming"
@toggle-block-outgoing="toggleBlockOutgoing"
@toggle-block-both="toggleBlockBoth"
/>
<csc-fax-item
v-else-if="item.type == 'fax'"
@ -16,9 +23,16 @@
v-else-if="item.type == 'voicemail'"
:voice-mail="item"
:call-available="callAvailable"
:block-incoming-label="blockIncomingLabel"
:block-outgoing-label="blockOutgoingLabel"
:block-both-label="blockBothLabel"
:block-both-possible="unblockedBoth || blockedBoth"
@download-voice-mail="downloadVoiceMail"
@play-voice-mail="playVoiceMail"
@start-call="startCall"
@toggle-block-incoming="toggleBlockIncoming"
@toggle-block-outgoing="toggleBlockOutgoing"
@toggle-block-both="toggleBlockBoth"
/>
</template>
@ -30,7 +44,9 @@
name: 'csc-conversation-item',
props: [
'item',
'callAvailable'
'callAvailable',
'blockedIncoming',
'blockedOutgoing'
],
components: {
CscCallItem,
@ -40,6 +56,52 @@
data () {
return {}
},
computed: {
number() {
if(this.item.direction === 'out') {
return this.item.callee;
}
else {
return this.item.caller;
}
},
toggleActionIncoming() {
return this.blockedIncoming ? 'unblock' : 'block';
},
toggleActionOutgoing() {
return this.blockedOutgoing ? 'unblock' : 'block';
},
blockIncomingLabel() {
if (this.blockedIncoming) {
return this.$t('pages.conversations.buttons.unblockIncoming');
}
else {
return this.$t('pages.conversations.buttons.blockIncoming');
}
},
blockOutgoingLabel() {
if (this.blockedOutgoing) {
return this.$t('pages.conversations.buttons.unblockOutgoing');
}
else {
return this.$t('pages.conversations.buttons.blockOutgoing');
}
},
blockBothLabel() {
if (this.blockedBoth) {
return this.$t('pages.conversations.buttons.unblockBoth');
}
else if (this.unblockedBoth) {
return this.$t('pages.conversations.buttons.blockBoth');
}
},
blockedBoth() {
return this.blockedIncoming && this.blockedOutgoing;
},
unblockedBoth() {
return !this.blockedIncoming && !this.blockedOutgoing;
}
},
methods: {
startCall(number) {
this.$emit('start-call', number);
@ -52,6 +114,24 @@
},
playVoiceMail(voiceMail) {
this.$emit('play-voice-mail', voiceMail);
},
toggleBlockIncoming() {
this.$emit('toggle-block-incoming', {
number: this.number,
type: this.toggleActionIncoming
});
},
toggleBlockOutgoing() {
this.$emit('toggle-block-outgoing', {
number: this.number,
type: this.toggleActionOutgoing
});
},
toggleBlockBoth() {
this.$emit('toggle-block-both', {
number: this.number,
type: this.toggleActionIncoming
});
}
}
}

@ -87,6 +87,40 @@
:label="$t('pages.conversations.buttons.call')"
/>
</q-item>
<q-item
@click="toggleBlockIncoming"
>
<q-item-side
icon="call received"
color="primary"
/>
<q-item-main
:label="blockIncomingLabel"
/>
</q-item>
<q-item
@click="toggleBlockOutgoing"
>
<q-item-side
icon="call made"
color="primary"
/>
<q-item-main
:label="blockOutgoingLabel"
/>
</q-item>
<q-item
v-if="blockBothPossible"
@click="toggleBlockBoth"
>
<q-item-side
icon="block"
color="primary"
/>
<q-item-main
:label="blockBothLabel"
/>
</q-item>
</q-list>
</q-popover>
</q-btn>
@ -114,7 +148,11 @@
name: 'csc-voice-mail-item',
props: [
'voiceMail',
'callAvailable'
'callAvailable',
'blockIncomingLabel',
'blockOutgoingLabel',
'blockBothLabel',
'blockBothPossible'
],
components: {
QList,
@ -174,6 +212,15 @@
startCall() {
this.$refs.callPopover.close();
this.$emit('start-call', this.voiceMail.callee);
},
toggleBlockIncoming() {
this.$emit('toggle-block-incoming');
},
toggleBlockOutgoing() {
this.$emit('toggle-block-outgoing');
},
toggleBlockBoth() {
this.$emit('toggle-block-both');
}
}
}

@ -140,7 +140,13 @@
"play": "Play",
"download": "Download",
"downloadFax": "Download fax",
"downloadVoicemail": "Download voicemail"
"downloadVoicemail": "Download voicemail",
"blockIncoming": "Block Incoming",
"unblockIncoming": "Unblock Incoming",
"blockOutgoing": "Block Outgoing",
"unblockOutgoing": "Unblock Outgoing",
"blockBoth": "Block Incoming/Outgoing",
"unblockBoth": "Unblock Incoming/Outgoing"
},
"downloadVoiceMailSuccessMessage": "Voicemail downloaded successfully",
"downloadVoiceMailErrorMessage": "Downloading of voicemail failed",
@ -161,7 +167,8 @@
"voicemail": "Voicemail",
"duration": "Duration",
"seconds": "seconds",
"cost": "Cost"
"cost": "Cost",
"toggledSuccessMessage": "Number {type} successfully"
},
"reminder": {
"toggleEnabled": "Reminder is enabled",

@ -7,8 +7,17 @@ import {
getConversations,
downloadVoiceMail,
downloadFax,
playVoiceMail
playVoiceMail,
getIncomingBlocked,
getOutgoingBlocked
} from '../api/conversations'
import {
addNumberToIncomingList,
removeFromIncomingListByNumber,
addNumberToOutgoingList,
removeFromOutgoingListByNumber,
toggleNumberInBothLists
} from '../api/call-blocking'
const ROWS_PER_PAGE = 15;
@ -62,7 +71,18 @@ export default {
nextPageState: RequestState.initiated,
nextPageError: null,
items: [],
itemsReloaded: false
itemsReloaded: false,
blockedNumbersIncoming: new Set(),
blockedModeIncoming: null,
blockedIncomingState: RequestState.initiated,
blockedIncomingError: null,
blockedNumbersOutgoing: new Set(),
blockedModeOutgoing: null,
blockedOutgoingState: RequestState.initiated,
blockedOutgoingError: null,
toggleBlockedState: RequestState.initiated,
toggleBlockedError: null,
lastToggledType: null
},
getters: {
getSubscriberId(state, getters, rootState, rootGetters) {
@ -114,6 +134,70 @@ export default {
},
itemsReloaded(state) {
return state.itemsReloaded;
},
isNumberIncomingBlocked(state) {
return (number) => {
if (state.blockedModeIncoming === 'whitelist') {
return !state.blockedNumbersIncoming.has(number);
}
else {
return state.blockedNumbersIncoming.has(number);
}
}
},
isNumberOutgoingBlocked(state) {
return (number) => {
if (state.blockedModeOutgoing === 'whitelist') {
return !state.blockedNumbersOutgoing.has(number);
}
else {
return state.blockedNumbersOutgoing.has(number);
}
}
},
blockedNumbersIncoming(state) {
return state.blockedNumbersIncoming;
},
blockedNumbersOutgoing(state) {
return state.blockedNumbersOutgoing;
},
blockedIncomingLoaded(state) {
return state.blockedIncomingState === RequestState.succeeded;
},
blockedOutgoingLoaded(state) {
return state.blockedOutgoingState === RequestState.succeeded;
},
isNumberIncomingWhitelisted(state) {
return state.blockedModeIncoming === 'whitelist';
},
isNumberOutgoingWhitelisted(state) {
return state.blockedModeOutgoing === 'whitelist';
},
actionToToggleIncomingNumber(state) {
return (number) => {
if (state.blockedNumbersIncoming.has(number)) {
return 'remove';
}
else {
return 'add';
}
}
},
actionToToggleOutgoingNumber(state) {
return (number) => {
if (state.blockedNumbersOutgoing.has(number)) {
return 'remove';
}
else {
return 'add';
}
}
},
toggleBlockedState(state) {
return state.toggleBlockedState;
},
lastToggledType(state) {
return state.lastToggledType;
}
},
mutations: {
@ -191,6 +275,58 @@ export default {
nextPageFailed(state, error) {
state.nextPageState = RequestState.failed;
state.nextPageError = error;
},
blockedIncomingRequesting(state) {
state.blockedIncomingState = RequestState.requesting;
state.blockedIncomingError = null;
state.blockedModeIncoming = null;
state.blockedNumbersIncoming = new Set();
},
blockedIncomingSucceeded(state, options) {
let mode = options.enabled ? 'whitelist' : 'blacklist';
let numbers = options.list ? options.list : [];
let numberSet = new Set(numbers);
state.blockedIncomingState = RequestState.succeeded;
state.blockedIncomingError = null;
state.blockedNumbersIncoming = numberSet;
state.blockedModeIncoming = mode;
},
blockedIncomingFailed(state, error) {
state.blockedIncomingState = RequestState.failed;
state.blockedIncomingError = error;
},
blockedOutgoingRequesting(state) {
state.blockedOutgoingState = RequestState.requesting;
state.blockedOutgoingError = null;
},
blockedOutgoingSucceeded(state, options) {
let mode = options.enabled ? 'whitelist' : 'blacklist';
let numbers = options.list ? options.list : [];
let numberSet = new Set(numbers);
state.blockedOutgoingState = RequestState.succeeded;
state.blockedOutgoingError = null;
state.blockedNumbersOutgoing = numberSet;
state.blockedModeOutgoing = mode;
},
blockedOutgoingFailed(state, error) {
state.blockedOutgoingState = RequestState.failed;
state.blockedOutgoingError = error;
},
toggleBlockedRequesting(state) {
state.toggleBlockedState = RequestState.requesting;
state.toggleBlockedError = null;
},
toggleBlockedSucceeded(state, type) {
let typePastTense = type ? type + 'ed' : 'toggled';
state.toggleBlockedState = RequestState.succeeded;
state.toggleBlockedError = null;
state.lastToggledType = typePastTense;
},
toggleBlockedFailed(state, error, type) {
let typePastTense = type ? type + 'ed' : 'toggled';
state.toggleBlockedState = RequestState.failed;
state.toggleBlockedError = error;
state.lastToggledType = typePastTense;
}
},
actions: {
@ -265,6 +401,116 @@ export default {
context.commit('nextPageFailed', err.message);
});
}
},
getBlockedNumbersIncoming(context) {
let id = context.getters.getSubscriberId;
context.commit('blockedIncomingRequesting');
getIncomingBlocked(id).then((data) => {
context.commit('blockedIncomingSucceeded', data);
}).catch((err)=>{
context.commit('blockedIncomingFailed', err.message);
});
},
getBlockedNumbersOutgoing(context) {
let id = context.getters.getSubscriberId;
context.commit('blockedOutgoingRequesting');
getOutgoingBlocked(id).then((data) => {
context.commit('blockedOutgoingSucceeded', data);
}).catch((err)=>{
context.commit('blockedOutgoingFailed', err.message);
});
},
getBlockedNumbers(context) {
context.dispatch('getBlockedNumbersIncoming');
context.dispatch('getBlockedNumbersOutgoing');
},
toggleBlockIncoming(context, options) {
let id = context.getters.getSubscriberId;
let isWhitelist = context.getters.isNumberIncomingWhitelisted;
let isBlocked = context.getters.isNumberIncomingBlocked(options.number);
context.commit('toggleBlockedRequesting');
if ((isBlocked && isWhitelist) || (!isBlocked && !isWhitelist)) {
addNumberToIncomingList(id, options.number).then(() => {
context.commit('toggleBlockedSucceeded', options.type);
}).then(() => {
context.dispatch('getBlockedNumbersIncoming');
}).then(() => {
context.commit('resetList');
context.dispatch('nextPage', null);
}).catch((err) => {
context.commit('toggleBlockedFailed', err.message, options.type);
});
}
else if ((isBlocked && !isWhitelist) || (!isBlocked && isWhitelist)) {
removeFromIncomingListByNumber(id, options.number).then(() => {
context.commit('toggleBlockedSucceeded', options.type);
}).then(() => {
context.dispatch('getBlockedNumbersIncoming');
}).then(() => {
context.commit('resetList');
context.dispatch('nextPage', null);
}).catch((err) => {
context.commit('toggleBlockedFailed', err.message, options.type);
});
}
else {
context.commit('toggleBlockedFailed', 'error while identifying blocked condition', options.type);
}
},
toggleBlockOutgoing(context, options) {
let id = context.getters.getSubscriberId;
let isWhitelist = context.getters.isNumberOutgoingWhitelisted;
let isBlocked = context.getters.isNumberOutgoingBlocked(options.number);
context.commit('toggleBlockedRequesting');
if ((isBlocked && isWhitelist) || (!isBlocked && !isWhitelist)) {
addNumberToOutgoingList(id, options.number).then(() => {
context.commit('toggleBlockedSucceeded', options.type);
}).then(() => {
context.dispatch('getBlockedNumbersOutgoing');
}).then(() => {
context.commit('resetList');
context.dispatch('nextPage', null);
}).catch((err) => {
context.commit('toggleBlockedFailed', err.message, options.type);
});
}
else if ((isBlocked && !isWhitelist) || (!isBlocked && isWhitelist)) {
removeFromOutgoingListByNumber(id, options.number).then(() => {
context.commit('toggleBlockedSucceeded', options.type);
}).then(() => {
context.dispatch('getBlockedNumbersOutgoing');
}).then(() => {
context.commit('resetList');
context.dispatch('nextPage', null);
}).catch((err) => {
context.commit('toggleBlockedFailed', err.message, options.type);
});
}
else {
context.commit('toggleBlockedFailed', 'error while identifying blocked condition', options.type);
}
},
toggleBlockBoth(context, options) {
let id = context.getters.getSubscriberId;
let inAction = context.getters.actionToToggleIncomingNumber(options.number);
let outAction = context.getters.actionToToggleOutgoingNumber(options.number);
context.commit('toggleBlockedRequesting');
toggleNumberInBothLists({
id: id,
number: options.number,
block_in_list: inAction,
block_out_list: outAction
}).then(() => {
context.commit('toggleBlockedSucceeded', options.type);
}).then(() => {
context.dispatch('getBlockedNumbersIncoming');
context.dispatch('getBlockedNumbersOutgoing');
}).then(() => {
context.commit('resetList');
context.dispatch('nextPage', null);
}).catch((err) => {
context.commit('toggleBlockedFailed', err.message, options.type);
});
}
}
};

@ -127,4 +127,28 @@ describe('Conversations', function(){
assert.deepEqual(state.items, data.items);
});
it('should load blocked numbers and mode', function(){
let state = {
blockedNumbersIncoming: new Set(),
blockedModeIncoming: null,
blockedNumbersOutgoing: new Set(),
blockedModeOutgoing: null
};
let options = {
blockAnonymous: undefined,
enabled: undefined,
list: [
"123456",
"555555"
]
};
let listSet = new Set(["123456", "555555"]);
ConversationsModule.mutations.blockedIncomingSucceeded(state, options);
ConversationsModule.mutations.blockedOutgoingSucceeded(state, options);
assert.deepEqual(state.blockedNumbersIncoming, listSet);
assert.equal(state.blockedModeIncoming, 'blacklist');
assert.deepEqual(state.blockedNumbersOutgoing, listSet);
assert.equal(state.blockedModeOutgoing, 'blacklist');
});
});

Loading…
Cancel
Save