TT#44662 Customer wants to play busy greeting

What is done:
- TT#45397, Implement greeting sound player methods and state handling
- TT#45389, Implement UI for playing "busy greeting sound", stop/play/pause
- TT#45390, Implement API method for fetching the playable "busy greeting sound"
- TT#45385, Implement API method for fetching the playable "unavailable greeting
sound"
- TT#45384, Implement UI for playing "unavailable greeting sound",
stop/play/pause
- TT#45398, Implement greeting sound player methods and state handling

Change-Id: I866bda3931c67cc79ad34d4be9e784f0705c221e
changes/70/24470/13
raxelsen 7 years ago committed by Hans-Peter Herzog
parent 8e1a96066c
commit 5a152e3967

@ -141,6 +141,21 @@ export function uploadGreeting(options) {
}
export function abortPreviousRequest(name) {
return new Promise((resolve) => {
let requestKey = `previous${_.capitalize(name)}Request`;
Vue[requestKey].abort();
resolve();
});
}
export function playGreeting(options) {
return new Promise((resolve, reject)=>{
let params = { format: options.format };
Vue.http.get(`api/voicemailgreetings/${options.id}`, { params: params, responseType: 'blob' })
.then((res) => {
resolve(URL.createObjectURL(res.body));
}).catch((err) => {
reject(err);
});
});
}

@ -1,13 +1,49 @@
<template>
<div class="voicemail-player">
<audio :src="fileUrl" ref="audio" preload="auto" @timeupdate="timeupdate($event)"/>
<div class="audio-player">
<audio
:src="fileUrl"
ref="audio"
preload="auto"
@timeupdate="timeUpdate"
/>
<div class="control-btns">
<q-btn class="play-pause-btn" round flat small color="primary"
:icon="playPauseIcon" @click="toggle()" />
<q-btn class="stop-btn" round flat small color="primary" icon="stop" @click="stop()"/>
<q-btn
v-if="pausable"
:disable="disable"
round
flat
small
color="primary"
:icon="playPauseIcon"
@click="toggle()"
/>
<q-btn
v-else
:disable="disable || playing"
round
flat
small
color="primary"
icon="play_arrow"
@click="playLoad()"
/>
<q-btn
:disable="disable || !pausable && !playing"
round
flat
small
color="primary"
icon="stop"
@click="stop()"
/>
</div>
<q-progress class="progress-bar" :percentage="progressPercentage" stripe animate color="primary"/>
<q-progress
class="progress-bar"
:percentage="progressPercentage"
stripe
animate
color="primary"
/>
</div>
</template>
@ -18,21 +54,23 @@
name: 'csc-audio-player',
props: [
'fileUrl',
'loaded'
'loaded',
'disable',
'pausable'
],
mounted() {
this.$refs.audio.addEventListener('play', ()=>{
this.$refs.audio.addEventListener('play', ()=> {
this.playing = true;
});
this.$refs.audio.addEventListener('playing', ()=>{
this.$refs.audio.addEventListener('playing', ()=> {
this.playing = true;
});
this.$refs.audio.addEventListener('ended', ()=>{
this.$refs.audio.addEventListener('ended', ()=> {
this.playing = false;
this.stop();
});
this.$refs.audio.addEventListener('canplay', ()=>{
if(!this.paused && this.playing) {
this.$refs.audio.addEventListener('canplay', ()=> {
if (!this.paused && this.playing) {
this.$refs.audio.play();
}
});
@ -58,6 +96,7 @@
this.$refs.audio.play();
this.playing = true;
this.paused = false;
this.$emit('playing');
},
pause() {
this.$refs.audio.pause();
@ -67,6 +106,7 @@
stop() {
this.$refs.audio.currentTime = 0;
this.pause();
this.$emit('stopped');
},
setPlayingTrue() {
this.playing = true;
@ -74,9 +114,8 @@
setPausedFalse() {
this.paused = false;
},
timeupdate(e) {
let newPercentage = Math.floor((e.target.currentTime / e.target.duration) * 100);
this.progressPercentage = newPercentage;
timeUpdate() {
this.progressPercentage = this.$refs.audio.currentTime * 100 / this.$refs.audio.duration;
},
load() {
this.$emit('load');
@ -91,6 +130,14 @@
else {
this.pause();
}
},
playLoad() {
if (!this.loaded) {
this.load();
}
else if (this.$refs.audio.paused) {
this.play();
}
}
}
}
@ -99,15 +146,17 @@
<style lang="stylus" rel="stylesheet/stylus">
@import '../themes/app.common'
.voicemail-player
.audio-player
width 100%
height 56px
display flex
justify-content space-around
align-items center
.control-btns
display flex
justify-content space-between
.progress-bar
margin-left 16px
margin-right 16px

@ -4,6 +4,7 @@
:icon="icon"
>
<q-input
:disable="isPlaying"
dark
readonly
:float-label="label"
@ -36,6 +37,16 @@
class="upload-progress"
/>
</div>
<csc-audio-player
ref="audioPlayer"
:file-url="fileUrl"
:loaded="loaded"
class="csc-greeting-player"
@load="init"
:disable="disablePlayer"
@playing="audioPlayerPlaying"
@stopped="audioPlayerStopped"
/>
<div
class="csc-file-upload-actions"
>
@ -58,6 +69,7 @@
{{ $t('buttons.upload') }}
</q-btn>
<q-btn
:disable="isPlaying"
flat
v-if="uploaded && selectedFile == null"
color="primary"
@ -71,6 +83,7 @@
</template>
<script>
import CscAudioPlayer from '../CscAudioPlayer'
import {
QInput,
QField,
@ -81,6 +94,7 @@
export default {
name: 'csc-sound-file-upload',
components: {
CscAudioPlayer,
QInput,
QField,
QBtn,
@ -94,14 +108,20 @@
'uploading',
'uploaded',
'progress',
'fileTypes'
'fileTypes',
'fileUrl',
'loaded'
],
data () {
return {
selectedFile: null
selectedFile: null,
isPlaying: false
}
},
computed: {
disablePlayer() {
return (this.selectedFile ? true : false) || !this.uploaded;
},
inputValue() {
if(this.selectedFile === null) {
return this.value;
@ -113,6 +133,17 @@
inputButtons() {
let buttons = [];
let self = this;
if (this.isPlaying) {
buttons.push({
icon: 'folder',
error: false,
handler (event) {
event.stopPropagation();
}
}
);
}
else {
buttons.push({
icon: 'folder',
error: false,
@ -122,10 +153,24 @@
}
}
);
}
return buttons;
}
},
methods: {
setPlayingTrue() {
this.$refs.audioPlayer.setPlayingTrue();
this.isPlaying = true;
},
setPausedFalse() {
this.$refs.audioPlayer.setPausedFalse();
},
audioPlayerPlaying() {
this.isPlaying = true;
},
audioPlayerStopped() {
this.isPlaying = false;
},
inputChange(event) {
this.selectedFile = event.target.files[0];
},
@ -147,6 +192,9 @@
},
undo() {
this.$emit('reset');
},
init() {
this.$emit('init');
}
}
}
@ -155,15 +203,15 @@
<style lang="stylus" rel="stylesheet/stylus">
@import '../../themes/quasar.variables';
.csc-file-upload-actions
padding-top $flex-gutter-xs
.csc-upload-field
margin-bottom 40px
.q-field-icon
color $primary
.csc-upload-field.csc-player-margin
margin-bottom 0
.csc-upload-progress-field
margin 10px 0 5px 0
@ -177,4 +225,7 @@
.upload-progress
height 20px
.csc-greeting-player
padding 0
</style>

@ -39,6 +39,7 @@
:loaded="voiceMailLoaded"
class="csc-voice-mail-player"
@load="load"
:pausable="true"
/>
</q-item-tile>
</q-item-main>

@ -20,7 +20,7 @@
:attachLabel="attachLabel"
/>
<csc-sound-file-upload
ref="uploadBusyGreeting"
ref="uploadBusy"
icon="music_note"
file-types=".wav,.mp3"
:label="$t('voicebox.label.busyGreeting')"
@ -31,9 +31,12 @@
@upload="uploadBusyGreeting"
@abort="abortBusy"
@reset="deleteBusy"
:file-url="playBusyGreetingUrl"
:loaded="playBusyGreetingLoaded"
@init="initBusyGreetingAudio"
/>
<csc-sound-file-upload
ref="uploadUnavailGreeting"
ref="uploadUnavail"
icon="music_note"
file-types=".wav,.mp3"
:label="$t('voicebox.label.unavailGreeting')"
@ -44,6 +47,9 @@
@upload="uploadUnavailGreeting"
@abort="abortUnavail"
@reset="deleteUnavail"
:file-url="playUnavailGreetingUrl"
:loaded="playUnavailGreetingLoaded"
@init="initUnavailGreetingAudio"
/>
</div>
</div>
@ -68,6 +74,7 @@
export default {
data () {
return {
platform: this.$q.platform.is
}
},
components: {
@ -117,16 +124,23 @@
'deleteGreetingState',
'deleteGreetingError',
'busyGreetingLabel',
'unavailGreetingLabel'
])
'unavailGreetingLabel',
'playBusyGreetingLoaded',
'playBusyGreetingUrl',
'playUnavailGreetingLoaded',
'playUnavailGreetingUrl'
]),
soundFileFormat() {
return this.platform.mozilla ? 'ogg' : 'mp3';
}
},
methods: {
resetBusyFile() {
this.$refs.uploadBusyGreeting.reset();
this.$refs.uploadBusy.reset();
this.$store.commit('voicebox/resetBusyProgress');
},
resetUnavailFile() {
this.$refs.uploadUnavailGreeting.reset();
this.$refs.uploadUnavail.reset();
this.$store.commit('voicebox/resetUnavailProgress');
},
uploadBusyGreeting(file) {
@ -140,10 +154,10 @@
});
},
abortBusy() {
this.$store.dispatch('voicebox/abortPreviousRequest', 'busy');
this.$store.dispatch('voicebox/abortUploadBusyGreeting');
},
abortUnavail() {
this.$store.dispatch('voicebox/abortPreviousRequest', 'unavail');
this.$store.dispatch('voicebox/abortUploadUnavailGreeting');
},
deleteBusy() {
let self = this;
@ -202,6 +216,22 @@
},
loadUnavailGreeting() {
this.$store.dispatch('voicebox/loadUnavailGreeting');
},
playBusyGreeting() {
this.$store.dispatch('voicebox/playBusyGreeting', this.soundFileFormat);
},
playUnavailGreeting() {
this.$store.dispatch('voicebox/playUnavailGreeting', this.soundFileFormat);
},
initBusyGreetingAudio() {
this.playBusyGreeting();
this.$refs.uploadBusy.setPlayingTrue();
this.$refs.uploadBusy.setPausedFalse();
},
initUnavailGreetingAudio() {
this.playUnavailGreeting();
this.$refs.uploadUnavail.setPlayingTrue();
this.$refs.uploadUnavail.setPausedFalse();
}
},
watch: {
@ -286,7 +316,4 @@
<style lang="stylus" rel="stylesheet/stylus">
@import '../../../themes/quasar.variables';
.csc-upload-file-label
margin-bottom $flex-gutter-sm
</style>

@ -226,15 +226,15 @@ export default {
},
downloadVoiceMail(context, id) {
context.commit('downloadVoiceMailRequesting');
downloadVoiceMail(id).then(()=>{
downloadVoiceMail(id).then(() => {
context.commit('downloadVoiceMailSucceeded');
}).catch((err)=>{
}).catch((err) => {
context.commit('downloadVoiceMailFailed', err.body.message);
});
},
downloadFax(context, id) {
context.commit('downloadFaxRequesting');
downloadFax(id).then(()=>{
downloadFax(id).then(() => {
context.commit('downloadFaxSucceeded');
}).catch((err)=>{
context.commit('downloadFaxFailed', err.body.message);
@ -242,12 +242,12 @@ export default {
},
playVoiceMail(context, options) {
context.commit('playVoiceMailRequesting', options.id);
playVoiceMail(options).then((url)=>{
playVoiceMail(options).then((url) => {
context.commit('playVoiceMailSucceeded', {
id: options.id,
url: url
});
}).catch((err)=>{
}).catch((err) => {
context.commit('playVoiceMailFailed', options.id, err.mesage);
});
},

@ -3,15 +3,15 @@
import { RequestState } from './common'
import {
getVoiceboxSettings,
setVoiceboxDelete,
getVoiceboxSettings, setVoiceboxDelete,
setVoiceboxAttach,
setVoiceboxPin,
setVoiceboxEmail,
uploadGreeting,
abortPreviousRequest,
getVoiceboxGreetingByType,
deleteVoiceboxGreetingById
deleteVoiceboxGreetingById,
playGreeting
} from '../api/voicebox';
import { i18n } from '../i18n';
@ -45,7 +45,13 @@ export default {
loadUnavailGreetingState: RequestState.initial,
loadUnavailGreetingError: null,
deleteGreetingState: RequestState.initial,
deleteGreetingError: null
deleteGreetingError: null,
playBusyGreetingUrl: null,
playBusyGreetingState: RequestState.initial,
playBusyGreetingError: null,
playUnavailGreetingUrl: null,
playUnavailGreetingState: RequestState.initial,
playUnavailGreetingError: null
},
getters: {
subscriberId(state, getters, rootState, rootGetters) {
@ -176,6 +182,18 @@ export default {
unavailGreetingLabel(state) {
return state.unavailGreetingId ? i18n.t('voicebox.label.customSoundActive') :
i18n.t('voicebox.label.defaultSoundActive')
},
playBusyGreetingLoaded(state) {
return state.playBusyGreetingState === 'succeeded';
},
playBusyGreetingUrl(state) {
return state.playBusyGreetingUrl;
},
playUnavailGreetingLoaded(state) {
return state.playUnavailGreetingState === 'succeeded';
},
playUnavailGreetingUrl(state) {
return state.playUnavailGreetingUrl;
}
},
mutations: {
@ -292,6 +310,7 @@ export default {
state.loadBusyGreetingError = null;
},
loadBusyGreetingSucceeded(state, greetings) {
state.playBusyGreetingState = RequestState.initial;
if (greetings.length > 0) {
state.busyGreetingId = greetings[0].id;
}
@ -308,6 +327,7 @@ export default {
state.loadUnavailGreetingError = null;
},
loadUnavailGreetingSucceeded(state, greetings) {
state.playUnavailGreetingState = RequestState.initial;
if (greetings.length > 0) {
state.unavailGreetingId = greetings[0].id;
}
@ -329,6 +349,34 @@ export default {
deleteGreetingFailed(state, error) {
state.deleteGreetingState = RequestState.failed;
state.deleteGreetingError = error;
},
playBusyGreetingRequesting(state) {
state.playBusyGreetingState = RequestState.requesting;
state.playBusyGreetingError = null;
},
playBusyGreetingSucceeded(state, url) {
state.playBusyGreetingUrl = url;
state.playBusyGreetingState = RequestState.succeeded;
state.playBusyGreetingError = null;
},
playBusyGreetingFailed(state, err) {
state.playBusyGreetingUrl = null;
state.playBusyGreetingState = RequestState.failed;
state.playBusyGreetingError = err;
},
playUnavailGreetingRequesting(state) {
state.playUnavailGreetingState = RequestState.requesting;
state.playUnavailGreetingError = null;
},
playUnavailGreetingSucceeded(state, url) {
state.playUnavailGreetingUrl = url;
state.playUnavailGreetingState = RequestState.succeeded;
state.playUnavailGreetingError = null;
},
playUnavailGreetingFailed(state, err) {
state.playUnavailGreetingUrl = null;
state.playUnavailGreetingState = RequestState.failed;
state.playUnavailGreetingError = err;
}
},
actions: {
@ -480,6 +528,38 @@ export default {
}).catch((err) => {
context.commit('deleteGreetingFailed', err.message);
});
},
playBusyGreeting(context, format) {
context.commit('playBusyGreetingRequesting');
playGreeting({
id: context.getters.busyGreetingId,
format: format
}).then((url) => {
context.commit('playBusyGreetingSucceeded', url);
}).catch((err) => {
context.commit('playBusyGreetingFailed', err.mesage);
});
},
playUnavailGreeting(context, format) {
context.commit('playUnavailGreetingRequesting');
playGreeting({
id: context.getters.unavailGreetingId,
format: format
}).then((url) => {
context.commit('playUnavailGreetingSucceeded', url);
}).catch((err) => {
context.commit('playUnavailGreetingFailed', err.mesage);
});
},
abortUploadBusyGreeting(context) {
abortPreviousRequest('busy').then(() => {
context.dispatch('loadBusyGreeting');
});
},
abortUploadUnavailGreeting(context) {
abortPreviousRequest('unavail').then(() => {
context.dispatch('loadUnavailGreeting');
});
}
}
};

@ -70,6 +70,24 @@ describe('Voicebox', function(){
assert.deepEqual(state.unavailGreetingId, greetings[0].id);
});
it('should load busy greeting url into store', function(){
let state = {
playBusyGreetingUrl: null
};
let url = "blob:https://1.2.3.4/6341147c-3ed2-4112-876b-331e834a4821";
VoiceboxModule.mutations.playBusyGreetingSucceeded(state, url);
assert.deepEqual(state.playBusyGreetingUrl, url);
});
it('should load unavailable greeting id into store', function(){
let state = {
playUnavailGreetingUrl: null
};
let url = "blob:https://1.2.3.4/6341147c-3ed2-4112-876b-331e834a4821";
VoiceboxModule.mutations.playUnavailGreetingSucceeded(state, url);
assert.deepEqual(state.playUnavailGreetingUrl, url);
});
it('should get right label for busy greeting to indicate if it\'s custom or default', function(){
let state = {
busyGreetingId: null

Loading…
Cancel
Save