mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-08-29 10:58:27 +00:00
feat(App): implement unread conversations count update and update telephony settings in the UI
This commit is contained in:
@@ -685,6 +685,7 @@ export default {
|
||||
ElectronUtils,
|
||||
reloadInterval: null,
|
||||
appInfoInterval: null,
|
||||
unreadCountInterval: null,
|
||||
|
||||
isShowingMyIdentitySection: true,
|
||||
isShowingAnnounceSection: true,
|
||||
@@ -915,6 +916,10 @@ export default {
|
||||
this.appInfoInterval = setInterval(() => {
|
||||
this.getAppInfo();
|
||||
}, 15000);
|
||||
this.unreadCountInterval = setInterval(() => {
|
||||
this.updateUnreadConversationsCount();
|
||||
}, 5000);
|
||||
this.updateUnreadConversationsCount();
|
||||
},
|
||||
stopShell() {
|
||||
if (!this.shellRunning) {
|
||||
@@ -925,6 +930,8 @@ export default {
|
||||
this.reloadInterval = null;
|
||||
clearInterval(this.appInfoInterval);
|
||||
this.appInfoInterval = null;
|
||||
clearInterval(this.unreadCountInterval);
|
||||
this.unreadCountInterval = null;
|
||||
WebSocketConnection.off("message", this.onWebsocketMessage);
|
||||
WebSocketConnection.off("disconnected", this.onWsShellDisconnected);
|
||||
WebSocketConnection.off("connected", this.onWsShellConnected);
|
||||
@@ -1084,6 +1091,21 @@ export default {
|
||||
onShowTutorialShell() {
|
||||
this.$refs.tutorialModal?.show();
|
||||
},
|
||||
updateUnreadConversationsCount() {
|
||||
if (this._unreadCountTimeout) {
|
||||
clearTimeout(this._unreadCountTimeout);
|
||||
}
|
||||
this._unreadCountTimeout = setTimeout(async () => {
|
||||
try {
|
||||
const response = await window.api.get("/api/v1/notifications", {
|
||||
params: { unread: true, limit: 1 },
|
||||
});
|
||||
GlobalState.unreadConversationsCount = response.data?.lxmf_total_unread_count ?? 0;
|
||||
} catch (e) {
|
||||
console.error("Failed to update unread conversations count", e);
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
onConfigUpdatedExternally(newConfig) {
|
||||
if (!newConfig || typeof newConfig !== "object") {
|
||||
return;
|
||||
@@ -1144,7 +1166,9 @@ export default {
|
||||
if (this.initiationStatus) {
|
||||
break;
|
||||
}
|
||||
NotificationUtils.showIncomingCallNotification();
|
||||
NotificationUtils.showIncomingCallNotification(
|
||||
json.remote_identity_name || json.remote_identity_hash
|
||||
);
|
||||
this.updateTelephoneStatus();
|
||||
this.playRingtone();
|
||||
break;
|
||||
@@ -1181,11 +1205,13 @@ export default {
|
||||
this.stopRingtone();
|
||||
this.ringtonePlayer = null;
|
||||
this.toneGenerator.stop();
|
||||
NotificationUtils.cancelIncomingCallNotification();
|
||||
this.updateTelephoneStatus();
|
||||
break;
|
||||
}
|
||||
case "telephone_call_ended": {
|
||||
this.stopRingtone();
|
||||
NotificationUtils.cancelIncomingCallNotification();
|
||||
this.ringtonePlayer = null;
|
||||
if (this.config?.telephone_tone_generator_enabled) {
|
||||
this.toneGenerator.setVolume(this.config.telephone_tone_generator_volume);
|
||||
@@ -1202,6 +1228,10 @@ export default {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update sidebar unread count so the badge appears
|
||||
// immediately even when not on the Messages page.
|
||||
this.updateUnreadConversationsCount();
|
||||
|
||||
// show notification for new messages if window is not focussed
|
||||
// only for incoming messages from people (with content)
|
||||
if (
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fixed max-sm:bottom-[calc(5.75rem+env(safe-area-inset-bottom,0px))] bottom-4 left-1/2 -translate-x-1/2 sm:left-auto sm:right-4 sm:translate-x-0 z-100 flex flex-col gap-2 pointer-events-none w-[calc(100%-2rem)] max-w-sm sm:w-auto sm:max-w-md"
|
||||
class="fixed max-sm:bottom-[calc(1rem+env(safe-area-inset-bottom,0px))] bottom-4 left-1/2 -translate-x-1/2 sm:left-auto sm:right-4 sm:translate-x-0 z-100 flex flex-col gap-2 pointer-events-none w-[calc(100%-2rem)] max-w-sm sm:w-auto sm:max-w-md"
|
||||
>
|
||||
<TransitionGroup name="toast">
|
||||
<div
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative z-10 space-y-1 mb-8">
|
||||
<div class="relative z-10 space-y-1 mb-8 flex flex-col items-center text-center">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white truncate max-w-[280px]">
|
||||
{{
|
||||
(activeCall || lastCall)?.remote_identity_name ||
|
||||
@@ -528,34 +528,36 @@
|
||||
class="pt-2 flex flex-col items-stretch gap-4 lg:flex-row lg:items-start lg:justify-between"
|
||||
>
|
||||
<div class="flex min-w-0 flex-1 flex-col gap-2">
|
||||
<Toggle
|
||||
id="dnd-toggle"
|
||||
:model-value="config?.do_not_disturb_enabled"
|
||||
:label="$t('call.do_not_disturb')"
|
||||
@update:model-value="toggleDoNotDisturb"
|
||||
/>
|
||||
<Toggle
|
||||
id="contacts-only-toggle"
|
||||
:model-value="config?.telephone_allow_calls_from_contacts_only"
|
||||
:label="$t('call.allow_calls_from_contacts_only')"
|
||||
@update:model-value="toggleAllowCallsFromContactsOnly"
|
||||
/>
|
||||
<Toggle
|
||||
id="telephone-announce-toggle"
|
||||
:model-value="config?.telephone_announce_enabled"
|
||||
label="Announce Telephone Presence (LXST)"
|
||||
@update:model-value="toggleTelephoneAnnounceEnabled"
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div v-if="config?.telephone_enabled" class="flex flex-col gap-2">
|
||||
<Toggle
|
||||
id="web-audio-toggle"
|
||||
:model-value="config?.telephone_web_audio_enabled"
|
||||
label="Web Audio Bridge"
|
||||
@update:model-value="onToggleWebAudio"
|
||||
id="dnd-toggle"
|
||||
:model-value="config?.do_not_disturb_enabled"
|
||||
:label="$t('call.do_not_disturb')"
|
||||
@update:model-value="toggleDoNotDisturb"
|
||||
/>
|
||||
<div class="text-xs text-gray-500 dark:text-zinc-400 px-1">
|
||||
Web audio bridge allows web/electron to hook into LXST backend for
|
||||
passing microphone and audio streams to active telephone calls.
|
||||
<Toggle
|
||||
id="contacts-only-toggle"
|
||||
:model-value="config?.telephone_allow_calls_from_contacts_only"
|
||||
:label="$t('call.allow_calls_from_contacts_only')"
|
||||
@update:model-value="toggleAllowCallsFromContactsOnly"
|
||||
/>
|
||||
<Toggle
|
||||
id="telephone-announce-toggle"
|
||||
:model-value="config?.telephone_announce_enabled"
|
||||
label="Announce Telephone Presence (LXST)"
|
||||
@update:model-value="toggleTelephoneAnnounceEnabled"
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<Toggle
|
||||
id="web-audio-toggle"
|
||||
:model-value="config?.telephone_web_audio_enabled"
|
||||
label="Web Audio Bridge"
|
||||
@update:model-value="onToggleWebAudio"
|
||||
/>
|
||||
<div class="text-xs text-gray-500 dark:text-zinc-400 px-1">
|
||||
Web audio bridge allows web/electron to hook into LXST backend for
|
||||
passing microphone and audio streams to active telephone calls.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2715,6 +2717,11 @@ export default {
|
||||
this.stopWebAudio();
|
||||
return;
|
||||
}
|
||||
// Do not start web audio during voicemail
|
||||
if (this.activeCall?.is_voicemail) {
|
||||
this.stopWebAudio();
|
||||
return;
|
||||
}
|
||||
if (this.activeCall && webAudioStatus.enabled) {
|
||||
this.audioFrameMs = webAudioStatus.frame_ms || 60;
|
||||
await this.startWebAudio();
|
||||
|
||||
@@ -1937,7 +1937,7 @@ export default {
|
||||
interface_discovery_whitelist: "",
|
||||
interface_discovery_blacklist: "",
|
||||
required_discovery_value: null,
|
||||
autoconnect_discovered_interfaces: 0,
|
||||
autoconnect_discovered_interfaces: null,
|
||||
default_bootstrap_only: true,
|
||||
network_identity: "",
|
||||
},
|
||||
|
||||
@@ -293,7 +293,9 @@ export default {
|
||||
}
|
||||
const st = this.iface._stats;
|
||||
if (!st || typeof st !== "object") {
|
||||
return null;
|
||||
// If stats are missing, the interface is likely detached or
|
||||
// not yet initialised; show down rather than unknown.
|
||||
return false;
|
||||
}
|
||||
if ("status" in st) {
|
||||
const s = st.status;
|
||||
|
||||
@@ -752,7 +752,7 @@ export default {
|
||||
interface_discovery_whitelist: "",
|
||||
interface_discovery_blacklist: "",
|
||||
required_discovery_value: null,
|
||||
autoconnect_discovered_interfaces: 0,
|
||||
autoconnect_discovered_interfaces: null,
|
||||
default_bootstrap_only: true,
|
||||
network_identity: "",
|
||||
},
|
||||
@@ -1242,7 +1242,7 @@ export default {
|
||||
discovery.autoconnect_discovered_interfaces !== null &&
|
||||
discovery.autoconnect_discovered_interfaces !== ""
|
||||
? Number(discovery.autoconnect_discovered_interfaces)
|
||||
: 0;
|
||||
: null;
|
||||
this.discoveryConfig.default_bootstrap_only = this.parseBool(discovery.default_bootstrap_only ?? true);
|
||||
this.discoveryConfig.network_identity = discovery.network_identity ?? "";
|
||||
} catch (e) {
|
||||
@@ -1266,7 +1266,7 @@ export default {
|
||||
autoconnect_discovered_interfaces:
|
||||
this.discoveryConfig.autoconnect_discovered_interfaces === null ||
|
||||
this.discoveryConfig.autoconnect_discovered_interfaces === ""
|
||||
? 0
|
||||
? null
|
||||
: Number(this.discoveryConfig.autoconnect_discovered_interfaces),
|
||||
default_bootstrap_only: this.discoveryConfig.default_bootstrap_only,
|
||||
network_identity: this.discoveryConfig.network_identity || null,
|
||||
|
||||
@@ -671,6 +671,36 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Telephony Settings -->
|
||||
<section
|
||||
v-show="matchesSearch(...sectionKeywords.telephony)"
|
||||
class="settings-section break-inside-avoid"
|
||||
>
|
||||
<header class="settings-section__header">
|
||||
<div>
|
||||
<div class="settings-section__eyebrow">Telephony</div>
|
||||
<h2>Telephone (LXST)</h2>
|
||||
<p>Enable or disable the integrated voice calling system.</p>
|
||||
</div>
|
||||
</header>
|
||||
<div class="settings-section__body space-y-4">
|
||||
<label class="setting-toggle">
|
||||
<Toggle
|
||||
id="telephone-enabled-toggle"
|
||||
v-model="config.telephone_enabled"
|
||||
@update:model-value="onTelephoneEnabledChange"
|
||||
/>
|
||||
<span class="setting-toggle__label">
|
||||
<span class="setting-toggle__title">Enable Telephone (LXST)</span>
|
||||
<span class="setting-toggle__description">
|
||||
Allow incoming and outgoing voice calls over the mesh network.
|
||||
</span>
|
||||
<span class="setting-toggle__hint">Disabling will end any active calls.</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Desktop / Electron Settings -->
|
||||
<section
|
||||
v-if="ElectronUtils.isElectron()"
|
||||
@@ -2669,6 +2699,16 @@ export default {
|
||||
visualiserShowDisabledInterfaces: false,
|
||||
visualiserShowDiscoveredInterfaces: false,
|
||||
sectionKeywords: {
|
||||
telephony: [
|
||||
"Telephony",
|
||||
"Telephone",
|
||||
"LXST",
|
||||
"Enable Telephone",
|
||||
"voice",
|
||||
"calling",
|
||||
"call",
|
||||
"mesh network",
|
||||
],
|
||||
strangerProtection: [
|
||||
"Security",
|
||||
"app.stranger_protection",
|
||||
@@ -3739,6 +3779,16 @@ export default {
|
||||
);
|
||||
}, 1000);
|
||||
},
|
||||
async onTelephoneEnabledChange(value) {
|
||||
this.config.telephone_enabled = value;
|
||||
try {
|
||||
const newConfig = await patchServerConfig({ telephone_enabled: value }, window.api);
|
||||
this.config = newConfig;
|
||||
ToastUtils.success(value ? "Telephone enabled" : "Telephone disabled");
|
||||
} catch {
|
||||
ToastUtils.error("Failed to update telephone setting");
|
||||
}
|
||||
},
|
||||
async onDesktopOpenCallsInSeparateWindowChange(value) {
|
||||
this.config.desktop_open_calls_in_separate_window = value;
|
||||
await this.updateConfig(
|
||||
|
||||
Reference in New Issue
Block a user