From 7d7cd7d48701e74ab38cef0db1eca1b0cd23cf87 Mon Sep 17 00:00:00 2001 From: Sudo-Ivan Date: Mon, 5 Jan 2026 19:22:25 -0600 Subject: [PATCH] feat(ui): enhance user experience with new features including QR code display, improved toast messages, and localized strings for various components --- meshchatx/src/frontend/components/App.vue | 88 ++- .../frontend/components/CommandPalette.vue | 10 + .../components/IntegrityWarningModal.vue | 28 +- meshchatx/src/frontend/components/Toast.vue | 2 +- .../src/frontend/components/TutorialModal.vue | 6 +- .../frontend/components/about/AboutPage.vue | 118 ++- .../components/blocked/BlockedPage.vue | 40 +- .../frontend/components/call/CallOverlay.vue | 12 +- .../src/frontend/components/call/CallPage.vue | 112 ++- .../components/call/RingtoneEditor.vue | 6 +- .../components/call/RingtoneEditorModal.vue | 6 +- .../components/debug/DebugLogsPage.vue | 6 +- .../src/frontend/components/docs/DocsPage.vue | 6 +- .../components/forwarder/ForwarderPage.vue | 5 +- .../interfaces/AddInterfacePage.vue | 8 +- .../interfaces/ImportInterfacesModal.vue | 14 +- .../components/interfaces/InterfacesPage.vue | 20 +- .../src/frontend/components/map/MapPage.vue | 75 +- .../messages/ConversationDropDownMenu.vue | 36 +- .../messages/ConversationViewer.vue | 44 +- .../components/messages/MessagesPage.vue | 36 +- .../components/messages/MessagesSidebar.vue | 21 +- .../components/messages/PaperMessageModal.vue | 10 +- .../micron-editor/MicronEditorPage.vue | 7 +- .../network-visualiser/NetworkVisualiser.vue | 195 ++++- .../nomadnetwork/NomadNetworkPage.vue | 4 +- .../nomadnetwork/NomadNetworkSidebar.vue | 720 ++++++++++++++---- .../components/profile/ProfileIconPage.vue | 16 +- .../PropagationNodesPage.vue | 2 +- .../components/settings/IdentitiesPage.vue | 15 +- .../components/settings/SettingsPage.vue | 277 ++++++- .../components/tools/PaperMessagePage.vue | 12 +- .../frontend/components/tools/RNPathPage.vue | 20 +- .../components/translator/TranslatorPage.vue | 14 +- meshchatx/src/frontend/locales/de.json | 328 +++++++- meshchatx/src/frontend/locales/en.json | 324 +++++++- meshchatx/src/frontend/locales/ru.json | 326 +++++++- tests/frontend/BanishedPage.test.js | 12 +- tests/frontend/MicronEditorPage.test.js | 13 +- tests/frontend/NetworkVisualiser.test.js | 10 + tests/frontend/Toast.test.js | 3 + 41 files changed, 2481 insertions(+), 526 deletions(-) diff --git a/meshchatx/src/frontend/components/App.vue b/meshchatx/src/frontend/components/App.vue index 3a7c273..7617259 100644 --- a/meshchatx/src/frontend/components/App.vue +++ b/meshchatx/src/frontend/components/App.vue @@ -133,7 +133,7 @@ ]" >
@@ -461,6 +473,51 @@ + +
+
+
+

LXMF Address QR

+ +
+
+
+ LXMF QR +
+
+ {{ config.lxmf_address_hash }} +
+
+ +
+
+
+
+
- Security Integrity Warning + {{ $t("about.security_integrity") }}

- Backend Tampering Detected!
- The application backend binary (unpacked from ASAR) appears to have been modified or replaced. This - could indicate a malicious actor trying to compromise your mesh communication. + {{ $t("about.tampering_detected") }}
+ {{ $t("about.integrity_backend_error") }}

- Data Tampering Detected!
- Your identities or database files appear to have been modified while the app was closed. + {{ $t("about.tampering_detected") }}
+ {{ $t("about.integrity_data_error") }}

- +
  • {{ issue }}
  • @@ -29,21 +30,20 @@

    - Proceed with caution. If you did not manually update or modify these files, your installation may be - compromised. + {{ $t("about.integrity_warning_footer") }}

    - Continue Anyway + {{ $t("common.continue") }} - Acknowledge & Reset + {{ $t("common.acknowledge_reset") }} @@ -104,10 +104,10 @@ export default { async acknowledgeAndReset() { try { await window.axios.post("/api/v1/app/integrity/acknowledge"); - ToastUtils.success("Integrity issues acknowledged and manifest reset"); + ToastUtils.success(this.$t("about.integrity_acknowledged_reset")); this.visible = false; } catch (e) { - ToastUtils.error("Failed to acknowledge integrity issues"); + ToastUtils.error(this.$t("about.failed_acknowledge_integrity")); console.error(e); } }, diff --git a/meshchatx/src/frontend/components/Toast.vue b/meshchatx/src/frontend/components/Toast.vue index 2c19876..1be846c 100644 --- a/meshchatx/src/frontend/components/Toast.vue +++ b/meshchatx/src/frontend/components/Toast.vue @@ -29,7 +29,7 @@
    - {{ toast.message }} + {{ $t(toast.message) }}
    diff --git a/meshchatx/src/frontend/components/TutorialModal.vue b/meshchatx/src/frontend/components/TutorialModal.vue index f3db212..89ecdd1 100644 --- a/meshchatx/src/frontend/components/TutorialModal.vue +++ b/meshchatx/src/frontend/components/TutorialModal.vue @@ -1405,12 +1405,12 @@ export default { autoconnect_discovered_interfaces: 3, // default to 3 slots }; await window.axios.patch(`/api/v1/reticulum/discovery`, payload); - ToastUtils.success("Community discovery enabled"); + ToastUtils.success(this.$t("tutorial.discovery_enabled")); this.discoveryOption = "yes"; this.nextStep(); } catch (e) { console.error("Failed to enable discovery:", e); - ToastUtils.error("Failed to enable discovery"); + ToastUtils.error(this.$t("tutorial.failed_enable_discovery")); } finally { this.savingDiscovery = false; } @@ -1527,7 +1527,7 @@ export default { ElectronUtils.relaunch(); } else { if (this.interfaceAddedViaTutorial) { - ToastUtils.info("Restart the application/container to apply changes."); + ToastUtils.info(this.$t("tutorial.ready_desc")); } this.visible = false; } diff --git a/meshchatx/src/frontend/components/about/AboutPage.vue b/meshchatx/src/frontend/components/about/AboutPage.vue index 9e28bb4..96f440f 100644 --- a/meshchatx/src/frontend/components/about/AboutPage.vue +++ b/meshchatx/src/frontend/components/about/AboutPage.vue @@ -46,9 +46,7 @@
-
+
A secure, resilient, and beautiful communications platform powered by the Reticulum Network Stack.
+ + +
+
+
+ Contact Developer +
+ +
+ + + +
+
+
+ LXMF Address +
+
+ 7cc8d66b4f6a0e0e49d34af7f6077b5a + +
+
+
+
+ Alternate +
+
+ 43d3309adf27fc446556121b553b56a6 + +
+
+
+ + Send to propagation node if you cant reach me! +
+
+
+
+
ElectronUtils.relaunch(), 2000); } } } catch { - ToastUtils.error("Failed to restore snapshot"); + ToastUtils.error(this.$t("about.failed_restore_snapshot")); } }, async getAppInfo() { @@ -1026,17 +1096,13 @@ export default { } }, async acknowledgeIntegrity() { - if ( - await DialogUtils.confirm( - "Are you sure you want to acknowledge these integrity issues? This will update the security manifest to match the current state of your files." - ) - ) { + if (await DialogUtils.confirm(this.$t("about.integrity_acknowledge_confirm"))) { try { await window.axios.post("/api/v1/app/integrity/acknowledge"); - ToastUtils.success("Integrity issues acknowledged"); + ToastUtils.success(this.$t("about.integrity_acknowledged")); await this.getAppInfo(); } catch { - ToastUtils.error("Failed to acknowledge integrity issues"); + ToastUtils.error(this.$t("about.failed_acknowledge_integrity")); } } }, @@ -1196,7 +1262,7 @@ export default { if (this.isElectron) { ElectronUtils.shutdown(); } else { - ToastUtils.success("Shutdown command sent to server."); + ToastUtils.success(this.$t("about.shutdown_sent")); } } }, @@ -1253,7 +1319,7 @@ export default { link.remove(); window.URL.revokeObjectURL(url); this.identityBackupMessage = "Identity downloaded. Keep it secret."; - ToastUtils.success("Identity key file exported"); + ToastUtils.success(this.$t("about.identity_exported")); } catch { this.identityBackupError = "Failed to download identity"; } @@ -1270,7 +1336,7 @@ export default { } await navigator.clipboard.writeText(this.identityBase32); this.identityBase32Message = "Identity copied. Clear your clipboard after use."; - ToastUtils.success("Identity Base32 key copied to clipboard"); + ToastUtils.success(this.$t("about.identity_copied")); } catch { this.identityBase32Error = "Failed to copy identity"; } diff --git a/meshchatx/src/frontend/components/blocked/BlockedPage.vue b/meshchatx/src/frontend/components/blocked/BlockedPage.vue index 08df701..76f281b 100644 --- a/meshchatx/src/frontend/components/blocked/BlockedPage.vue +++ b/meshchatx/src/frontend/components/blocked/BlockedPage.vue @@ -8,8 +8,8 @@
-

Banished

-

Manage Banished users and nodes

+

{{ $t("banishment.title") }}

+

{{ $t("banishment.description") }}

@@ -22,13 +22,13 @@ v-model="searchQuery" type="text" class="block w-full pl-10 pr-3 py-2 border border-gray-300 dark:border-zinc-700 rounded-lg bg-gray-50 dark:bg-zinc-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all" - placeholder="Search by hash or display name..." + :placeholder="$t('banishment.search_placeholder')" @input="onSearchInput" />
@@ -85,19 +81,19 @@ class="text-base font-semibold text-gray-900 dark:text-white break-words" :title="item.display_name" > - {{ item.display_name || "Unknown" }} + {{ item.display_name || $t("call.unknown") }} - Node + {{ $t("banishment.node") }} - User + {{ $t("banishment.user") }}
- Banished {{ formatTimeAgo(item.created_at) }} + {{ $t("banishment.banished_at") }} {{ formatTimeAgo(item.created_at) }}
- Lift Banishment + {{ $t("banishment.lift_banishment") }}
@@ -212,7 +208,7 @@ export default { } const processItem = async (hash, data = {}) => { - let displayName = "Unknown"; + let displayName = this.$t("call.unknown"); let isNode = false; try { @@ -226,7 +222,7 @@ export default { if (announceResponse.data.announces && announceResponse.data.announces.length > 0) { const announce = announceResponse.data.announces[0]; - displayName = announce.display_name || "Unknown"; + displayName = announce.display_name || this.$t("call.unknown"); isNode = announce.aspect === "nomadnetwork.node"; } } catch { @@ -261,7 +257,7 @@ export default { this.reticulumBlackholedItems = rnsItems; } catch (e) { console.log(e); - ToastUtils.error("Failed to load banished destinations"); + ToastUtils.error(this.$t("banishment.failed_load_banished")); } finally { this.isLoading = false; } @@ -269,7 +265,7 @@ export default { async onUnblock(item) { if ( !(await DialogUtils.confirm( - `Are you sure you want to lift the banishment for ${item.display_name || item.destination_hash}?` + this.$t("banishment.lift_banishment_confirm", { name: item.display_name || item.destination_hash }) )) ) { return; @@ -278,10 +274,10 @@ export default { try { await window.axios.delete(`/api/v1/blocked-destinations/${item.destination_hash}`); await this.loadBlockedDestinations(); - ToastUtils.success("Banishment lifted successfully"); + ToastUtils.success(this.$t("banishment.banishment_lifted")); } catch (e) { console.log(e); - ToastUtils.error("Failed to lift banishment"); + ToastUtils.error(this.$t("banishment.failed_lift_banishment")); } }, onSearchInput() {}, diff --git a/meshchatx/src/frontend/components/call/CallOverlay.vue b/meshchatx/src/frontend/components/call/CallOverlay.vue index 1e07e9b..e95afa1 100644 --- a/meshchatx/src/frontend/components/call/CallOverlay.vue +++ b/meshchatx/src/frontend/components/call/CallOverlay.vue @@ -409,7 +409,7 @@ export default { try { await window.axios.get("/api/v1/telephone/answer"); } catch { - ToastUtils.error("Failed to answer call"); + ToastUtils.error(this.$t("call.failed_to_answer_call")); } }, async hangupCall() { @@ -417,15 +417,15 @@ export default { this.$emit("hangup"); await window.axios.get("/api/v1/telephone/hangup"); } catch { - ToastUtils.error("Failed to hangup call"); + ToastUtils.error(this.$t("call.failed_to_hangup_call")); } }, async sendToVoicemail() { try { await window.axios.get("/api/v1/telephone/send-to-voicemail"); - ToastUtils.success("Call sent to voicemail"); + ToastUtils.success(this.$t("call.call_sent_to_voicemail")); } catch { - ToastUtils.error("Failed to send call to voicemail"); + ToastUtils.error(this.$t("call.failed_to_send_to_voicemail")); } }, async toggleMicrophone() { @@ -449,7 +449,7 @@ export default { this.isMicMuting = false; // Revert on error this.localMicMuted = !this.localMicMuted; - ToastUtils.error("Failed to toggle microphone"); + ToastUtils.error(this.$t("call.failed_to_toggle_microphone")); } }, async toggleSpeaker() { @@ -473,7 +473,7 @@ export default { this.isSpeakerMuting = false; // Revert on error this.localSpeakerMuted = !this.localSpeakerMuted; - ToastUtils.error("Failed to toggle speaker"); + ToastUtils.error(this.$t("call.failed_to_toggle_speaker")); } }, async playLatestVoicemail() { diff --git a/meshchatx/src/frontend/components/call/CallPage.vue b/meshchatx/src/frontend/components/call/CallPage.vue index e0f0e0d..fc82b14 100644 --- a/meshchatx/src/frontend/components/call/CallPage.vue +++ b/meshchatx/src/frontend/components/call/CallPage.vue @@ -2481,7 +2481,7 @@ export default { this.refreshAudioDevices(); } catch (err) { console.error("Web audio failed", err); - ToastUtils.error("Web audio not available"); + ToastUtils.error(this.$t("call.web_audio_not_available")); this.stopWebAudio(); } }, @@ -2596,9 +2596,9 @@ export default { if (response.data?.config) { this.config = response.data.config; } - ToastUtils.success("Settings saved"); + ToastUtils.success(this.$t("call.settings_saved")); } catch { - ToastUtils.error("Failed to save settings"); + ToastUtils.error(this.$t("call.failed_to_save_settings")); } }, async getAudioProfiles() { @@ -2641,6 +2641,8 @@ export default { await this.ensureWebAudio(response.data.web_audio); } + this.hydrateContactVisuals(); + // If call just ended, refresh history and show ended state if (oldCall != null && this.activeCall == null) { this.getHistory(); @@ -2706,6 +2708,7 @@ export default { } this.hasMoreCallHistory = newItems.length === this.callHistoryLimit; + this.hydrateContactVisuals(); } catch (e) { console.log(e); } @@ -2769,7 +2772,7 @@ export default { } ToastUtils.success(value ? "Do Not Disturb enabled" : "Do Not Disturb disabled"); } catch { - ToastUtils.error("Failed to update Do Not Disturb status"); + ToastUtils.error(this.$t("call.failed_to_update_dnd")); } }, async toggleAllowCallsFromContactsOnly(value) { @@ -2782,7 +2785,7 @@ export default { } ToastUtils.success(value ? "Calls limited to contacts" : "Calls allowed from everyone"); } catch { - ToastUtils.error("Failed to update call settings"); + ToastUtils.error(this.$t("call.failed_to_update_call_settings")); } }, async toggleCallRecording(value) { @@ -2795,7 +2798,7 @@ export default { } ToastUtils.success(value ? "Call recording enabled" : "Call recording disabled"); } catch { - ToastUtils.error("Failed to update call recording status"); + ToastUtils.error(this.$t("call.failed_to_update_recording_status")); } }, async clearHistory() { @@ -2803,10 +2806,10 @@ export default { try { await window.axios.delete("/api/v1/telephone/history"); this.callHistory = []; - ToastUtils.success("Call history cleared"); + ToastUtils.success(this.$t("call.call_history_cleared")); } catch (e) { console.error(e); - ToastUtils.error("Failed to clear call history"); + ToastUtils.error(this.$t("call.failed_to_clear_call_history")); } }, async blockIdentity(hash) { @@ -2815,10 +2818,10 @@ export default { await window.axios.post("/api/v1/blocked-destinations", { destination_hash: hash, }); - ToastUtils.success("Identity banished"); + ToastUtils.success(this.$t("call.identity_banished")); this.getHistory(); } catch { - ToastUtils.error("Failed to banish identity"); + ToastUtils.error(this.$t("call.failed_to_banish_identity")); } }, async getVoicemailStatus() { @@ -2976,6 +2979,7 @@ export default { params: { search: this.contactsSearch }, }); this.contacts = response.data.contacts || (Array.isArray(response.data) ? response.data : []); + this.hydrateContactVisuals(); } catch (e) { console.log(e); } @@ -2986,6 +2990,40 @@ export default { this.getContacts(); }, 300); }, + hydrateContactVisuals() { + const map = {}; + this.contacts.forEach((c) => { + if (!c) return; + const image = c.custom_image; + const keys = [c.remote_identity_hash, c.lxmf_address, c.lxst_address].filter(Boolean); + keys.forEach((k) => { + map[k] = image; + }); + }); + + const applyImage = (target) => { + if (!target) return; + const key = + target.remote_identity_hash || target.remote_destination_hash || target.remote_telephony_hash; + if (key && map[key]) { + target.custom_image = map[key]; + } + }; + + applyImage(this.activeCall); + applyImage(this.lastCall); + + if (Array.isArray(this.callHistory) && this.callHistory.length > 0) { + this.callHistory = this.callHistory.map((entry) => { + const key = + entry.remote_identity_hash || entry.remote_destination_hash || entry.remote_telephony_hash; + if (key && map[key]) { + return { ...entry, contact_image: map[key] }; + } + return entry; + }); + } + }, openAddContactModal() { this.editingContact = null; this.contactForm = { @@ -3013,7 +3051,7 @@ export default { }, async saveContact(contact) { if (!contact.name || !contact.remote_identity_hash) { - ToastUtils.error("Name and identity hash required"); + ToastUtils.error(this.$t("call.name_and_hash_required")); return; } try { @@ -3023,10 +3061,10 @@ export default { contact.clear_image = true; } await window.axios.patch(`/api/v1/telephone/contacts/${contact.id}`, contact); - ToastUtils.success("Contact updated"); + ToastUtils.success(this.$t("call.contact_updated")); } else { await window.axios.post("/api/v1/telephone/contacts", contact); - ToastUtils.success("Contact added"); + ToastUtils.success(this.$t("call.contact_added")); } this.isContactModalOpen = false; this.getContacts(); @@ -3038,10 +3076,10 @@ export default { if (!confirm("Are you sure you want to delete this contact?")) return; try { await window.axios.delete(`/api/v1/telephone/contacts/${contactId}`); - ToastUtils.success("Contact deleted"); + ToastUtils.success(this.$t("call.contact_deleted")); this.getContacts(); } catch { - ToastUtils.error("Failed to delete contact"); + ToastUtils.error(this.$t("call.failed_to_delete_contact")); } }, onContactImageChange(event) { @@ -3069,17 +3107,17 @@ export default { async copyHash(hash) { try { await navigator.clipboard.writeText(hash); - ToastUtils.success("Hash copied to clipboard"); + ToastUtils.success(this.$t("call.hash_copied")); } catch (e) { console.error(e); - ToastUtils.error("Failed to copy hash"); + ToastUtils.error(this.$t("call.failed_to_copy_hash")); } }, async generateGreeting() { this.isGeneratingGreeting = true; try { await window.axios.post("/api/v1/telephone/voicemail/generate-greeting"); - ToastUtils.success("Greeting generated successfully"); + ToastUtils.success(this.$t("call.greeting_generated_successfully")); await this.getVoicemailStatus(); } catch (e) { ToastUtils.error(e.response?.data?.message || "Failed to generate greeting"); @@ -3101,7 +3139,7 @@ export default { "Content-Type": "multipart/form-data", }, }); - ToastUtils.success("Greeting uploaded successfully"); + ToastUtils.success(this.$t("call.greeting_uploaded_successfully")); await this.getVoicemailStatus(); } catch (e) { ToastUtils.error(e.response?.data?.message || "Failed to upload greeting"); @@ -3115,10 +3153,10 @@ export default { try { await window.axios.delete("/api/v1/telephone/voicemail/greeting"); - ToastUtils.success("Greeting deleted"); + ToastUtils.success(this.$t("call.greeting_deleted")); await this.getVoicemailStatus(); } catch { - ToastUtils.error("Failed to delete greeting"); + ToastUtils.error(this.$t("call.failed_to_delete_greeting")); } }, async startRecordingGreetingMic() { @@ -3126,16 +3164,16 @@ export default { await window.axios.post("/api/v1/telephone/voicemail/greeting/record/start"); await this.getVoicemailStatus(); } catch { - ToastUtils.error("Failed to start recording greeting"); + ToastUtils.error(this.$t("call.failed_to_start_recording_greeting")); } }, async stopRecordingGreetingMic() { try { await window.axios.post("/api/v1/telephone/voicemail/greeting/record/stop"); await this.getVoicemailStatus(); - ToastUtils.success("Greeting recorded from mic"); + ToastUtils.success(this.$t("call.greeting_recorded_from_mic")); } catch { - ToastUtils.error("Failed to stop recording greeting"); + ToastUtils.error(this.$t("call.failed_to_stop_recording_greeting")); } }, async playVoicemail(voicemail) { @@ -3195,9 +3233,9 @@ export default { try { await window.axios.delete(`/api/v1/telephone/voicemails/${voicemailId}`); this.getVoicemails(); - ToastUtils.success("Voicemail deleted"); + ToastUtils.success(this.$t("call.voicemail_deleted")); } catch { - ToastUtils.error("Failed to delete voicemail"); + ToastUtils.error(this.$t("call.failed_to_delete_voicemail")); } }, async getRecordings() { @@ -3244,7 +3282,7 @@ export default { await this.audioPlayer.play(); } catch (e) { console.error("Failed to play recording:", e); - ToastUtils.error("Failed to load recording audio"); + ToastUtils.error(this.$t("call.failed_to_load_recording")); this.playingRecordingId = null; this.playingSide = null; } @@ -3254,9 +3292,9 @@ export default { try { await window.axios.delete(`/api/v1/telephone/recordings/${recordingId}`); this.getRecordings(); - ToastUtils.success("Recording deleted"); + ToastUtils.success(this.$t("call.recording_deleted")); } catch { - ToastUtils.error("Failed to delete recording"); + ToastUtils.error(this.$t("call.failed_to_delete_recording")); } }, async playGreeting() { @@ -3282,7 +3320,7 @@ export default { }, async call(identityHash) { if (!identityHash) { - ToastUtils.error("Enter an identity to call"); + ToastUtils.error(this.$t("call.enter_identity_hash_to_call_error")); return; } @@ -3360,7 +3398,7 @@ export default { try { await window.axios.get("/api/v1/telephone/answer"); } catch { - ToastUtils.error("Failed to answer call"); + ToastUtils.error(this.$t("call.failed_to_answer_call")); } }, async hangupCall() { @@ -3370,22 +3408,22 @@ export default { } await window.axios.get("/api/v1/telephone/hangup"); } catch { - ToastUtils.error("Failed to hangup call"); + ToastUtils.error(this.$t("call.failed_to_hangup_call")); } }, async sendToVoicemail() { try { await window.axios.get("/api/v1/telephone/send-to-voicemail"); - ToastUtils.success("Call sent to voicemail"); + ToastUtils.success(this.$t("call.call_sent_to_voicemail")); } catch { - ToastUtils.error("Failed to send call to voicemail"); + ToastUtils.error(this.$t("call.failed_to_send_to_voicemail")); } }, async switchAudioProfile(audioProfileId) { try { await window.axios.get(`/api/v1/telephone/switch-audio-profile/${audioProfileId}`); } catch { - ToastUtils.error("Failed to switch audio profile"); + ToastUtils.error(this.$t("call.failed_to_switch_audio_profile")); } }, async toggleMicrophone() { @@ -3412,7 +3450,7 @@ export default { this.isMicMuting = false; // Revert on error this.localMicMuted = !this.localMicMuted; - ToastUtils.error("Failed to toggle microphone"); + ToastUtils.error(this.$t("call.failed_to_toggle_microphone")); } }, async toggleSpeaker() { @@ -3439,7 +3477,7 @@ export default { this.isSpeakerMuting = false; // Revert on error this.localSpeakerMuted = !this.localSpeakerMuted; - ToastUtils.error("Failed to toggle speaker"); + ToastUtils.error(this.$t("call.failed_to_toggle_speaker")); } }, }, diff --git a/meshchatx/src/frontend/components/call/RingtoneEditor.vue b/meshchatx/src/frontend/components/call/RingtoneEditor.vue index b4e6ede..4dd8f22 100644 --- a/meshchatx/src/frontend/components/call/RingtoneEditor.vue +++ b/meshchatx/src/frontend/components/call/RingtoneEditor.vue @@ -217,7 +217,7 @@ export default { }); } catch (e) { console.error("Failed to load audio:", e); - ToastUtils.error("Failed to load audio for editing"); + ToastUtils.error(this.$t("call.failed_load_audio_edit")); this.$emit("close"); } finally { this.loading = false; @@ -418,12 +418,12 @@ export default { headers: { "Content-Type": "multipart/form-data" }, }); - ToastUtils.success("Ringtone saved successfully"); + ToastUtils.success(this.$t("call.ringtone_saved")); this.$emit("saved"); this.$emit("close"); } catch (e) { console.error("Failed to save ringtone:", e); - ToastUtils.error("Failed to save edited ringtone"); + ToastUtils.error(this.$t("call.failed_save_ringtone")); } finally { this.saving = false; } diff --git a/meshchatx/src/frontend/components/call/RingtoneEditorModal.vue b/meshchatx/src/frontend/components/call/RingtoneEditorModal.vue index 0d2e428..695bc91 100644 --- a/meshchatx/src/frontend/components/call/RingtoneEditorModal.vue +++ b/meshchatx/src/frontend/components/call/RingtoneEditorModal.vue @@ -233,7 +233,7 @@ export default { }); } catch (e) { console.error("Failed to load audio:", e); - ToastUtils.error("Failed to load audio for editing"); + ToastUtils.error(this.$t("call.failed_load_audio_edit")); this.$emit("close"); } finally { this.loading = false; @@ -449,12 +449,12 @@ export default { // await window.axios.delete(`/api/v1/telephone/ringtones/${this.ringtone.id}`); } - ToastUtils.success("Ringtone saved successfully"); + ToastUtils.success(this.$t("call.ringtone_saved")); this.$emit("saved"); this.$emit("close"); } catch (e) { console.error("Failed to save ringtone:", e); - ToastUtils.error("Failed to save edited ringtone"); + ToastUtils.error(this.$t("call.failed_save_ringtone")); } finally { this.saving = false; } diff --git a/meshchatx/src/frontend/components/debug/DebugLogsPage.vue b/meshchatx/src/frontend/components/debug/DebugLogsPage.vue index bc63f7b..a35ef72 100644 --- a/meshchatx/src/frontend/components/debug/DebugLogsPage.vue +++ b/meshchatx/src/frontend/components/debug/DebugLogsPage.vue @@ -208,7 +208,7 @@ export default { this.total = response.data.total; } catch (e) { console.log("Failed to fetch logs", e); - if (!silent) ToastUtils.error("Failed to fetch logs"); + if (!silent) ToastUtils.error(this.$t("debug.failed_fetch_logs")); } finally { if (!silent) this.loading = false; } @@ -259,9 +259,9 @@ export default { .join("\n"); try { await navigator.clipboard.writeText(logText); - ToastUtils.success("Logs on this page copied to clipboard"); + ToastUtils.success(this.$t("debug.logs_copied")); } catch { - ToastUtils.error("Failed to copy logs"); + ToastUtils.error(this.$t("debug.failed_copy_logs")); } }, }, diff --git a/meshchatx/src/frontend/components/docs/DocsPage.vue b/meshchatx/src/frontend/components/docs/DocsPage.vue index 52c4e67..9da4ecb 100644 --- a/meshchatx/src/frontend/components/docs/DocsPage.vue +++ b/meshchatx/src/frontend/components/docs/DocsPage.vue @@ -691,7 +691,7 @@ export default { await this.updateDocs(); } catch (error) { console.error("Failed to add alternate source:", error); - ToastUtils.error("Failed to update documentation sources"); + ToastUtils.error(this.$t("docs.failed_update_docs")); } }, async switchVersion(version) { @@ -743,10 +743,10 @@ export default { navigator.clipboard .writeText(url) .then(() => { - ToastUtils.success("Documentation link copied to clipboard"); + ToastUtils.success(this.$t("docs.docs_link_copied")); }) .catch(() => { - ToastUtils.error("Failed to copy link"); + ToastUtils.error(this.$t("docs.failed_copy_link")); }); }, async setLanguage(langCode) { diff --git a/meshchatx/src/frontend/components/forwarder/ForwarderPage.vue b/meshchatx/src/frontend/components/forwarder/ForwarderPage.vue index 01f8b19..9761d00 100644 --- a/meshchatx/src/frontend/components/forwarder/ForwarderPage.vue +++ b/meshchatx/src/frontend/components/forwarder/ForwarderPage.vue @@ -152,6 +152,7 @@