feat(contacts): add functionality to check if a contact exists by identity hash and update UI to display contact status in call overlays

This commit is contained in:
2026-01-01 20:46:18 -06:00
parent d9f3e9b094
commit 5d3d5114a5
4 changed files with 62 additions and 10 deletions

View File

@@ -3040,6 +3040,7 @@ class ReticulumMeshChat:
"is_speaker_muted": self.telephone_manager.telephone.receive_muted,
"is_voicemail": self.voicemail_manager.is_recording,
"call_start_time": self.telephone_manager.call_start_time,
"is_contact": bool(self.database.contacts.get_contact_by_identity_hash(remote_identity_hash)) if remote_identity_hash else False,
}
return web.json_response(
@@ -3150,6 +3151,7 @@ class ReticulumMeshChat:
icon = self.database.misc.get_user_icon(lxmf_hash)
if icon:
d["remote_icon"] = dict(icon)
d["is_contact"] = bool(self.database.contacts.get_contact_by_identity_hash(remote_identity_hash))
call_history.append(d)
return web.json_response(
@@ -3631,6 +3633,12 @@ class ReticulumMeshChat:
self.database.contacts.delete_contact(contact_id)
return web.json_response({"message": "Contact deleted"})
@routes.get("/api/v1/telephone/contacts/check/{identity_hash}")
async def telephone_contacts_check(request):
identity_hash = request.match_info["identity_hash"]
contact = self.database.contacts.get_contact_by_identity_hash(identity_hash)
return web.json_response({"is_contact": contact is not None, "contact": dict(contact) if contact else None})
# announce
@routes.get("/api/v1/announce")
async def announce_trigger(request):

View File

@@ -58,3 +58,9 @@ class ContactsDAO:
def delete_contact(self, contact_id):
self.provider.execute("DELETE FROM contacts WHERE id = ?", (contact_id,))
def get_contact_by_identity_hash(self, remote_identity_hash):
return self.provider.fetchone(
"SELECT * FROM contacts WHERE remote_identity_hash = ?",
(remote_identity_hash,),
)

View File

@@ -69,6 +69,12 @@
<div class="font-bold text-gray-900 dark:text-white truncate px-2">
{{ activeCall.remote_identity_name || $t("call.unknown") }}
</div>
<div
v-if="activeCall.is_contact"
class="text-[10px] text-blue-600 dark:text-blue-400 font-medium mt-0.5"
>
In contacts
</div>
<div class="text-[10px] text-gray-500 dark:text-zinc-500 font-mono truncate px-4">
{{
activeCall.remote_identity_hash

View File

@@ -100,6 +100,12 @@
}}</span>
<span v-else>{{ $t("call.unknown") }}</span>
</div>
<div
v-if="(activeCall || lastCall)?.is_contact"
class="text-xs text-blue-600 dark:text-blue-400 font-medium mt-1"
>
In contacts
</div>
<!-- identity hash -->
<div
@@ -393,16 +399,27 @@
{{ entry.remote_identity_hash }}
</div>
</div>
<button
type="button"
class="text-[10px] text-blue-500 hover:text-blue-600 font-bold uppercase tracking-tighter ml-4"
@click="
destinationHash = entry.remote_identity_hash;
call(destinationHash);
"
>
Call Back
</button>
<div class="flex items-center gap-1">
<button
v-if="!entry.is_contact"
type="button"
class="p-1.5 text-gray-400 hover:text-blue-500 transition-colors"
:title="'Add to contacts'"
@click="addContactFromHistory(entry)"
>
<MaterialDesignIcon icon-name="account-plus" class="size-4" />
</button>
<button
type="button"
class="text-[10px] text-blue-500 hover:text-blue-600 font-bold uppercase tracking-tighter"
@click="
destinationHash = entry.remote_identity_hash;
call(destinationHash);
"
>
Call Back
</button>
</div>
</div>
</div>
</div>
@@ -1212,6 +1229,21 @@ export default {
console.log(e);
}
},
async addContactFromHistory(entry) {
const name = prompt("Enter contact name:", entry.remote_identity_name || "");
if (!name) return;
try {
await window.axios.post("/api/v1/telephone/contacts", {
name: name,
remote_identity_hash: entry.remote_identity_hash,
});
ToastUtils.success("Contact added");
this.getHistory();
this.getContacts();
} catch (e) {
ToastUtils.error(e.response?.data?.message || "Failed to add contact");
}
},
async getHistory() {
try {
const response = await window.axios.get("/api/v1/telephone/history?limit=10");