feat(call): implement elapsed time display for active calls and improve UI responsiveness with updated button styles
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="activeCall"
|
||||
class="fixed bottom-4 right-4 z-[100] w-72 bg-white dark:bg-zinc-900 rounded-2xl shadow-2xl border border-gray-200 dark:border-zinc-800 overflow-hidden transition-all duration-300"
|
||||
:class="{ 'ring-2 ring-red-500 ring-opacity-50': isEnded }"
|
||||
class="fixed bottom-4 right-4 z-[100] w-80 bg-white dark:bg-zinc-900 rounded-2xl shadow-2xl border border-gray-200 dark:border-zinc-800 overflow-hidden transition-all duration-300"
|
||||
:class="{ 'ring-2 ring-red-500 ring-opacity-50': isEnded || wasDeclined }"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="p-3 flex items-center bg-gray-50 dark:bg-zinc-800/50 border-b border-gray-100 dark:border-zinc-800">
|
||||
@@ -105,6 +105,12 @@
|
||||
<span v-else-if="activeCall.status === 6">{{ $t("call.connected") }}</span>
|
||||
<span v-else>{{ $t("call.status") }}: {{ activeCall.status }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="activeCall.status === 6 && !isEnded && elapsedTime"
|
||||
class="text-xs text-gray-500 dark:text-zinc-400 mt-1 font-mono"
|
||||
>
|
||||
{{ elapsedTime }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats (only when connected and not minimized) -->
|
||||
@@ -123,11 +129,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div v-if="!isEnded" class="flex justify-center space-x-3">
|
||||
<div v-if="!isEnded && !wasDeclined" class="flex flex-wrap justify-center gap-3">
|
||||
<!-- Mute Mic -->
|
||||
<button
|
||||
type="button"
|
||||
:title="isMicMuted ? 'Unmute Mic' : 'Mute Mic'"
|
||||
:title="isMicMuted ? $t('call.unmute_mic') : $t('call.mute_mic')"
|
||||
class="p-3 rounded-full transition-all duration-200"
|
||||
:class="
|
||||
isMicMuted
|
||||
@@ -142,7 +148,7 @@
|
||||
<!-- Mute Speaker -->
|
||||
<button
|
||||
type="button"
|
||||
:title="isSpeakerMuted ? 'Unmute Speaker' : 'Mute Speaker'"
|
||||
:title="isSpeakerMuted ? $t('call.unmute_speaker') : $t('call.mute_speaker')"
|
||||
class="p-3 rounded-full transition-all duration-200"
|
||||
:class="
|
||||
isSpeakerMuted
|
||||
@@ -206,9 +212,17 @@
|
||||
class="size-5 shrink-0"
|
||||
/>
|
||||
<MaterialDesignIcon v-else icon-name="account" class="size-5 text-blue-500 shrink-0" />
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-zinc-200 truncate block">
|
||||
{{ activeCall.remote_identity_name || $t("call.unknown") }}
|
||||
</span>
|
||||
<div class="flex flex-col min-w-0">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-zinc-200 truncate block">
|
||||
{{ activeCall.remote_identity_name || $t("call.unknown") }}
|
||||
</span>
|
||||
<span
|
||||
v-if="activeCall.status === 6 && elapsedTime"
|
||||
class="text-[10px] text-gray-500 dark:text-zinc-400 font-mono"
|
||||
>
|
||||
{{ elapsedTime }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<button
|
||||
@@ -260,6 +274,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
isMinimized: false,
|
||||
elapsedTimeInterval: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -269,6 +284,23 @@ export default {
|
||||
isSpeakerMuted() {
|
||||
return this.activeCall?.is_speaker_muted ?? false;
|
||||
},
|
||||
elapsedTime() {
|
||||
if (!this.activeCall?.call_start_time) {
|
||||
return null;
|
||||
}
|
||||
const elapsed = Math.floor(Date.now() / 1000 - this.activeCall.call_start_time);
|
||||
return Utils.formatMinutesSeconds(elapsed);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.elapsedTimeInterval = setInterval(() => {
|
||||
this.$forceUpdate();
|
||||
}, 1000);
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.elapsedTimeInterval) {
|
||||
clearInterval(this.elapsedTimeInterval);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatDestinationHash(hash) {
|
||||
|
||||
@@ -135,6 +135,12 @@
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
v-if="activeCall && activeCall.status === 6 && !isCallEnded && elapsedTime"
|
||||
class="text-gray-500 dark:text-zinc-400 mb-4 text-center font-mono text-lg"
|
||||
>
|
||||
{{ elapsedTime }}
|
||||
</div>
|
||||
|
||||
<!-- settings during connected call -->
|
||||
<div v-if="activeCall && activeCall.status === 6" class="mb-4">
|
||||
@@ -209,16 +215,16 @@
|
||||
</div>
|
||||
|
||||
<!-- actions -->
|
||||
<div v-if="activeCall" class="mx-auto space-x-4">
|
||||
<div v-if="activeCall" class="flex flex-wrap justify-center gap-4 mt-6">
|
||||
<!-- answer call -->
|
||||
<button
|
||||
v-if="activeCall.is_incoming && activeCall.status === 4"
|
||||
:title="$t('call.answer_call')"
|
||||
type="button"
|
||||
class="inline-flex items-center gap-x-2 rounded-2xl bg-green-600 px-6 py-4 text-lg font-bold text-white shadow-xl hover:bg-green-500 transition-all duration-200 animate-bounce"
|
||||
class="inline-flex items-center gap-x-2 rounded-2xl bg-green-600 px-5 py-3 text-base font-bold text-white shadow-xl hover:bg-green-500 transition-all duration-200 animate-bounce"
|
||||
@click="answerCall"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="phone" class="size-6" />
|
||||
<MaterialDesignIcon icon-name="phone" class="size-5" />
|
||||
<span>{{ $t("call.accept") }}</span>
|
||||
</button>
|
||||
|
||||
@@ -227,10 +233,10 @@
|
||||
v-if="activeCall.is_incoming && activeCall.status === 4"
|
||||
:title="$t('call.send_to_voicemail')"
|
||||
type="button"
|
||||
class="inline-flex items-center gap-x-2 rounded-2xl bg-blue-600 px-6 py-4 text-lg font-bold text-white shadow-xl hover:bg-blue-500 transition-all duration-200"
|
||||
class="inline-flex items-center gap-x-2 rounded-2xl bg-blue-600 px-5 py-3 text-base font-bold text-white shadow-xl hover:bg-blue-500 transition-all duration-200"
|
||||
@click="sendToVoicemail"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="voicemail" class="size-6" />
|
||||
<MaterialDesignIcon icon-name="voicemail" class="size-5" />
|
||||
<span>{{ $t("call.send_to_voicemail") }}</span>
|
||||
</button>
|
||||
|
||||
@@ -242,10 +248,10 @@
|
||||
: $t('call.hangup_call')
|
||||
"
|
||||
type="button"
|
||||
class="inline-flex items-center gap-x-2 rounded-2xl bg-red-600 px-6 py-4 text-lg font-bold text-white shadow-xl hover:bg-red-500 transition-all duration-200"
|
||||
class="inline-flex items-center gap-x-2 rounded-2xl bg-red-600 px-5 py-3 text-base font-bold text-white shadow-xl hover:bg-red-500 transition-all duration-200"
|
||||
@click="hangupCall"
|
||||
>
|
||||
<MaterialDesignIcon icon-name="phone-hangup" class="size-6 rotate-[135deg]" />
|
||||
<MaterialDesignIcon icon-name="phone-hangup" class="size-5 rotate-[135deg]" />
|
||||
<span>{{
|
||||
activeCall.is_incoming && activeCall.status === 4
|
||||
? $t("call.decline")
|
||||
@@ -926,6 +932,7 @@ export default {
|
||||
ringtones: [],
|
||||
editingRingtoneId: null,
|
||||
editingRingtoneName: "",
|
||||
elapsedTimeInterval: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -935,6 +942,13 @@ export default {
|
||||
isSpeakerMuted() {
|
||||
return this.activeCall?.is_speaker_muted ?? false;
|
||||
},
|
||||
elapsedTime() {
|
||||
if (!this.activeCall?.call_start_time) {
|
||||
return null;
|
||||
}
|
||||
const elapsed = Math.floor(Date.now() / 1000 - this.activeCall.call_start_time);
|
||||
return Utils.formatMinutesSeconds(elapsed);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getConfig();
|
||||
@@ -959,6 +973,11 @@ export default {
|
||||
this.getVoicemails();
|
||||
}, 10000);
|
||||
|
||||
// update elapsed time every second
|
||||
this.elapsedTimeInterval = setInterval(() => {
|
||||
this.$forceUpdate();
|
||||
}, 1000);
|
||||
|
||||
// autofill destination hash from query string
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const destinationHash = urlParams.get("destination_hash");
|
||||
@@ -969,6 +988,7 @@ export default {
|
||||
beforeUnmount() {
|
||||
if (this.statusInterval) clearInterval(this.statusInterval);
|
||||
if (this.historyInterval) clearInterval(this.historyInterval);
|
||||
if (this.elapsedTimeInterval) clearInterval(this.elapsedTimeInterval);
|
||||
if (this.endedTimeout) clearTimeout(this.endedTimeout);
|
||||
if (this.audioPlayer) {
|
||||
this.audioPlayer.pause();
|
||||
|
||||
Reference in New Issue
Block a user