diff --git a/meshchatx/src/frontend/components/App.vue b/meshchatx/src/frontend/components/App.vue index 898ebfa..506902a 100644 --- a/meshchatx/src/frontend/components/App.vue +++ b/meshchatx/src/frontend/components/App.vue @@ -52,29 +52,29 @@ > -
+
- -
+
+ {{ callDuration }} +
@@ -298,6 +301,13 @@ export default { const elapsed = Math.floor(Date.now() / 1000 - this.activeCall.call_start_time); return Utils.formatMinutesSeconds(elapsed); }, + callDuration() { + if (!this.isEnded || !this.activeCall?.call_start_time) { + return null; + } + const duration = Math.floor(Date.now() / 1000 - this.activeCall.call_start_time); + return Utils.formatMinutesSeconds(duration); + }, }, mounted() { this.elapsedTimeInterval = setInterval(() => { diff --git a/meshchatx/src/frontend/components/call/CallPage.vue b/meshchatx/src/frontend/components/call/CallPage.vue index b282b71..fa316bc 100644 --- a/meshchatx/src/frontend/components/call/CallPage.vue +++ b/meshchatx/src/frontend/components/call/CallPage.vue @@ -53,21 +53,19 @@ > {{ $t("call.ringtone") }} -
+
+ +
+
@@ -158,6 +156,12 @@ > {{ elapsedTime }}
+
+ {{ callDuration }} +
@@ -298,7 +302,7 @@
Telephone
-
Enter an identity hash to call.
+
Enter an identity to call.
@@ -324,21 +328,36 @@
-
-

- Call History -

-
- - + Call History + +
+ + +
+
+
+ +
    @@ -409,6 +428,14 @@ > +
@@ -425,6 +452,18 @@
+
+ +
@@ -446,6 +485,208 @@
+ +
+ + +
+ +
+ +
+

Dependencies Missing

+

+ Voicemail requires `espeak-ng` to generate greetings. Please install it on your + system. +

+

+ Voicemail requires `ffmpeg` to process audio files. Please install it on your + system. +

+
+
+ + +
+
+
+ Enable Voicemail +
+
+ Accept calls automatically and record messages +
+
+ +
+ + +
+ + +
+

+ This text will be converted to speech using eSpeak NG. +

+
+ +
+
+
+ + +
+ +
+ + +
+ + +
+
+ No custom greeting uploaded (default text will be used) +
+
+

+ Supports MP3, OGG, WAV, M4A, FLAC. Will be converted to Opus. +

+
+ + +
+
+ + +
+
+ + +
+
+
+
+
@@ -748,13 +989,13 @@
-
+
{{ ringtone.display_name }} @@ -785,7 +1027,10 @@
-
+
{{ ringtone.filename }}
@@ -843,196 +1088,6 @@
- - -
-
-

- - Voicemail Settings -

- - -
- -
-

Dependencies Missing

-

- Voicemail requires `espeak-ng` to generate greetings. Please install it on your - system. -

-

- Voicemail requires `ffmpeg` to process audio files. Please install it on your - system. -

-
-
- -
- -
-
-
- Enable Voicemail -
-
- Accept calls automatically and record messages -
-
- -
- - -
- - -
-

- This text will be converted to speech using eSpeak NG. -

-
- -
-
-
- - -
- -
- - -
- - -
-
- No custom greeting uploaded (default text will be used) -
-
-

- Supports MP3, OGG, WAV, M4A, FLAC. Will be converted to Opus. -

-
- - -
-
- - -
-
- - -
-
-
-
-
@@ -1042,11 +1097,12 @@ import Utils from "../../js/Utils"; import MaterialDesignIcon from "../MaterialDesignIcon.vue"; import LxmfUserIcon from "../LxmfUserIcon.vue"; +import Toggle from "../forms/Toggle.vue"; import ToastUtils from "../../js/ToastUtils"; export default { name: "CallPage", - components: { MaterialDesignIcon, LxmfUserIcon }, + components: { MaterialDesignIcon, LxmfUserIcon, Toggle }, data() { return { config: null, @@ -1056,6 +1112,10 @@ export default { destinationHash: "", isShowingStats: false, callHistory: [], + callHistorySearch: "", + callHistoryLimit: 10, + callHistoryOffset: 0, + hasMoreCallHistory: true, isCallEnded: false, wasDeclined: false, lastCall: null, @@ -1094,6 +1154,7 @@ export default { remote_identity_hash: "", }, searchDebounceTimeout: null, + isVoicemailSettingsExpanded: false, }; }, computed: { @@ -1110,6 +1171,13 @@ export default { const elapsed = Math.floor(Date.now() / 1000 - this.activeCall.call_start_time); return Utils.formatMinutesSeconds(elapsed); }, + callDuration() { + if (!this.isCallEnded || !this.lastCall?.call_start_time) { + return null; + } + const duration = Math.floor(Date.now() / 1000 - this.lastCall.call_start_time); + return Utils.formatMinutesSeconds(duration); + }, }, mounted() { this.getConfig(); @@ -1244,14 +1312,55 @@ export default { ToastUtils.error(e.response?.data?.message || "Failed to add contact"); } }, - async getHistory() { + async getHistory(loadMore = false) { try { - const response = await window.axios.get("/api/v1/telephone/history?limit=10"); - this.callHistory = response.data.call_history; + if (!loadMore) { + this.callHistoryOffset = 0; + this.hasMoreCallHistory = true; + } + + const response = await window.axios.get( + `/api/v1/telephone/history?limit=${this.callHistoryLimit}&offset=${this.callHistoryOffset}${ + this.callHistorySearch ? `&search=${encodeURIComponent(this.callHistorySearch)}` : "" + }` + ); + + const newItems = response.data.call_history; + if (loadMore) { + this.callHistory = [...this.callHistory, ...newItems]; + } else { + this.callHistory = newItems; + } + + this.hasMoreCallHistory = newItems.length === this.callHistoryLimit; } catch (e) { console.log(e); } }, + async loadMoreCallHistory() { + this.callHistoryOffset += this.callHistoryLimit; + await this.getHistory(true); + }, + onCallHistorySearchInput() { + if (this.searchDebounceTimeout) clearTimeout(this.searchDebounceTimeout); + this.searchDebounceTimeout = setTimeout(() => { + this.getHistory(); + }, 500); + }, + async toggleDoNotDisturb(value) { + try { + await window.axios.post("/api/v1/config", { + key: "do_not_disturb_enabled", + value: value ? "true" : "false", + }); + if (this.config) { + this.config.do_not_disturb_enabled = value; + } + ToastUtils.success(value ? "Do Not Disturb enabled" : "Do Not Disturb disabled"); + } catch { + ToastUtils.error("Failed to update Do Not Disturb status"); + } + }, async clearHistory() { if (!confirm(this.$t("common.delete_confirm"))) return; try { @@ -1263,6 +1372,18 @@ export default { ToastUtils.error("Failed to clear call history"); } }, + async blockIdentity(hash) { + if (!confirm(`Are you sure you want to block this identity?`)) return; + try { + await window.axios.post("/api/v1/blocked-destinations", { + destination_hash: hash, + }); + ToastUtils.success("Identity blocked"); + this.getHistory(); + } catch { + ToastUtils.error("Failed to block identity"); + } + }, async getVoicemailStatus() { try { const response = await window.axios.get("/api/v1/telephone/voicemail/status"); @@ -1577,7 +1698,7 @@ export default { }, async call(identityHash) { if (!identityHash) { - ToastUtils.error("Enter an identity hash to call"); + ToastUtils.error("Enter an identity to call"); return; } try {