diff --git a/src/api/call-blocking.js b/src/api/call-blocking.js
index 72972d75..4071d808 100644
--- a/src/api/call-blocking.js
+++ b/src/api/call-blocking.js
@@ -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);
+ });
+ });
+}
diff --git a/src/api/conversations.js b/src/api/conversations.js
index 9a544d50..8b0af2d5 100644
--- a/src/api/conversations.js
+++ b/src/api/conversations.js
@@ -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);
+ });
+ });
+}
+
diff --git a/src/components/pages/Conversations/Conversations.vue b/src/components/pages/Conversations/Conversations.vue
index 669306a6..1fd6452d 100644
--- a/src/components/pages/Conversations/Conversations.vue
+++ b/src/components/pages/Conversations/Conversations.vue
@@ -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"
/>
+
+
+
+
+
+
+
+
+
+
+
+
@@ -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');
}
}
}
diff --git a/src/components/pages/Conversations/CscConversationItem.vue b/src/components/pages/Conversations/CscConversationItem.vue
index 7f1fe057..3fe79ede 100644
--- a/src/components/pages/Conversations/CscConversationItem.vue
+++ b/src/components/pages/Conversations/CscConversationItem.vue
@@ -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"
/>
@@ -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
+ });
}
}
}
diff --git a/src/components/pages/Conversations/CscVoiceMailItem.vue b/src/components/pages/Conversations/CscVoiceMailItem.vue
index f48f35c6..8f09e456 100644
--- a/src/components/pages/Conversations/CscVoiceMailItem.vue
+++ b/src/components/pages/Conversations/CscVoiceMailItem.vue
@@ -87,6 +87,40 @@
:label="$t('pages.conversations.buttons.call')"
/>
+
+
+
+
+
+
+
+
+
+
+
+
@@ -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');
}
}
}
diff --git a/src/locales/en.json b/src/locales/en.json
index db411747..d9845b0a 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -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",
diff --git a/src/store/conversations.js b/src/store/conversations.js
index 702f7e65..9a6dbcf9 100644
--- a/src/store/conversations.js
+++ b/src/store/conversations.js
@@ -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);
+ });
}
}
};
diff --git a/t/store/conversations.js b/t/store/conversations.js
index 271cf2da..c02013d1 100644
--- a/t/store/conversations.js
+++ b/t/store/conversations.js
@@ -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');
+ });
+
});