fix(ui): DM contact picker missed real contacts (off-by-MAX_ANON_CONTACTS)

getContactByIdx() indexes the raw contacts[] table directly, whose first
MAX_ANON_CONTACTS (8) slots are reserved for anon requests -- getNumContacts()
already excludes them from its count, so real contacts start at index
MAX_ANON_CONTACTS, not 0 (NearbyScreen.h's own contact scan already applies
this offset; its comment documents why).

MessagesScreen.h's buildContactList() didn't, so its loop only ever read
the reserved anon slots (empty name, type 0) for any total at or under
MAX_ANON_CONTACTS -- e.g. a device with exactly one known contact would
show "SELECT CONTACT" / "No favourites" with an empty list forever,
regardless of the dm_show_all/fav_only setting or that contact's own
favourite flag. Reproduced live in the browser sim: a fresh companion_radio
instance with one real ADV_TYPE_CHAT contact showed nothing until this fix.
Also fixed _sorted[]'s stored index (was the bare loop counter, needed to
be the raw table index every other call site in the file already assumes)
and the same missing-offset bug in BotScreen.h's room-contact counter and
MessageHistory.h's pub_key-prefix contact lookup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-03 12:37:57 +02:00
co-authored by Claude Sonnet 5
parent b067e95b24
commit 0cb02ee18f
3 changed files with 29 additions and 4 deletions
+7 -1
View File
@@ -96,8 +96,14 @@ class BotScreen : public UIScreen {
_num_rooms = 0;
ContactInfo ci;
int total = the_mesh.getNumContacts();
// +MAX_ANON_CONTACTS: getContactByIdx() indexes the raw contacts[] table,
// whose first MAX_ANON_CONTACTS slots are reserved anon-request entries
// that getNumContacts() already excludes from its count (see
// NearbyScreen.h's own contact scan, and MessagesScreen.h's
// buildContactList(), for the same offset) -- without it this underrode
// real room-server contacts by up to MAX_ANON_CONTACTS.
for (int i = 0; i < total; i++)
if (the_mesh.getContactByIdx(i, ci) && ci.type == ADV_TYPE_ROOM) _num_rooms++;
if (the_mesh.getContactByIdx(MAX_ANON_CONTACTS + i, ci) && ci.type == ADV_TYPE_ROOM) _num_rooms++;
}
// Header as a circular tab bar (shared geometry — see TabBar.h). `right_reserve`
@@ -388,9 +388,12 @@ private:
// Look up a contact by 4-byte pub_key prefix (as stored in DmHistEntry).
bool contactByPrefix(const uint8_t* prefix, ContactInfo& out) const {
int total = the_mesh.getNumContacts();
// +MAX_ANON_CONTACTS: see MessagesScreen.h's buildContactList() for why
// getContactByIdx() needs this offset (raw table index, not the
// anon-excluded logical count getNumContacts() returns).
for (int i = 0; i < total; i++) {
ContactInfo c;
if (!the_mesh.getContactByIdx(i, c)) continue;
if (!the_mesh.getContactByIdx(MAX_ANON_CONTACTS + i, c)) continue;
if (memcmp(c.id.pub_key, prefix, 4) == 0) { out = c; return true; }
}
return false;
@@ -694,8 +694,20 @@ class MessagesScreen : public UIScreen {
// task stack for one local array.
uint8_t keys[MAX_CONTACTS];
bool fav_only = rooms ? (p && p->room_fav_only) : !(p && p->dm_show_all);
// getContactByIdx() indexes the RAW contacts[] table, whose first
// MAX_ANON_CONTACTS slots are reserved for anon requests (see
// BaseChatMesh::resetContacts()/ContactsIterator); getNumContacts()
// already excludes them from the count, so the real contacts start at
// MAX_ANON_CONTACTS, not 0 -- same offset NearbyScreen.h's own contact
// scan already applies. Without it, this loop only ever read the
// reserved anon slots (empty name, type 0) for the first
// MAX_ANON_CONTACTS iterations and, for any total at or under that count,
// NEVER reached a real contact at all -- e.g. a device with exactly one
// known contact would show "No favourites" with an empty list forever,
// regardless of the dm_show_all/fav_only setting or that contact's own
// favourite flag.
for (int i = 0; i < total; i++) {
if (!the_mesh.getContactByIdx(i, c)) continue;
if (!the_mesh.getContactByIdx(MAX_ANON_CONTACTS + i, c)) continue;
if (c.type != (rooms ? ADV_TYPE_ROOM : ADV_TYPE_CHAT)) continue;
bool fav = (c.flags & 0x01) != 0;
if (fav_only && !fav) continue;
@@ -703,7 +715,11 @@ class MessagesScreen : public UIScreen {
if (k > 127) k = 127;
if (fav && !(p && p->fav_sort_off)) k += 128;
keys[_num_contacts] = k;
_sorted[_num_contacts++] = i;
// Store the RAW table index (not the bare loop counter) -- every other
// call site in this file (render()'s mesh_idx, toggleFav(), the
// context-menu handlers, ...) reads _sorted[...] straight into
// getContactByIdx() with no offset of its own.
_sorted[_num_contacts++] = MAX_ANON_CONTACTS + i;
}
// Descending insertion sort; rows with key 0 keep their contact-table order.
for (int i = 1; i < _num_contacts; i++) {