fix(companion): also clear stale contact refs on silent auto-eviction

onContactOverwrite() (the contact-table-full LRU eviction path) deleted the
contact's blob and notified the companion app, but never called the new
onContactRemoved() cleanup -- so a Favourites Dial slot, Locator target, or
Live Share target could still go stale, just via the silent auto-evict path
instead of an explicit removal. This is likely the main real-world cause of
the "(gone)" tile the docs described, since auto-eviction happens far more
often than an explicit CMD_REMOVE_CONTACT.

Also sync the two docs that described the old (now wrong) behaviour:
Locator's "survives delete" claim, and Favourites Dial's "(gone) until
reassigned" claim.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekZegare4
2026-06-27 00:31:06 +02:00
parent 3c34809af7
commit 43c3f43e10
4 changed files with 8 additions and 5 deletions

View File

@@ -29,7 +29,7 @@ Navigate tiles with **UP / DOWN / LEFT / RIGHT**. Pressing a directional key at
Filled tiles show an unread message count in the top-right corner when there are unread DMs from that contact. The contact name is ellipsized to make room for the badge. Filled tiles show an unread message count in the top-right corner when there are unread DMs from that contact. The contact name is ellipsized to make room for the badge.
If a pinned contact has been removed from the contacts list, the tile shows `(gone)` until the slot is reassigned. If a pinned contact is removed from the contacts list — explicitly, or auto-evicted to make room when the table is full — its slot is freed automatically and goes back to an empty `+` tile.
--- ---

View File

@@ -253,7 +253,7 @@ The tool holds both directions of sharing in one flat list. Navigate with **UP/D
<!-- screenshot pending: Locator screen with a target set (e.g. "@Bob (5m)"), radius/mode/beeper rows --> <!-- screenshot pending: Locator screen with a target set (e.g. "@Bob (5m)"), radius/mode/beeper rows -->
A single **geofence** that beeps and shows an alert when you cross **into** or **out of** a radius. The target can be a **saved waypoint** (a fixed place — "tell me when I'm back at camp") or a **live contact** (a person sharing their position via Live Share — "alert me when my friend gets near / falls behind"). A waypoint target is a **snapshot** (coordinate + label copied), so it keeps working even if you later edit or delete that waypoint; a contact target follows the person's latest shared position. A single **geofence** that beeps and shows an alert when you cross **into** or **out of** a radius. The target can be a **saved waypoint** (a fixed place — "tell me when I'm back at camp") or a **live contact** (a person sharing their position via Live Share — "alert me when my friend gets near / falls behind"). A waypoint target is a **snapshot** (coordinate + label copied), so it keeps working even if you later edit that waypoint; a contact target follows the person's latest shared position. **Deleting** the target's waypoint, or the target contact being removed from the contacts list, clears the Locator target back to `none` instead of leaving it pointed at something that's gone.
Navigate with **UP/DOWN**, change a value with **LEFT/RIGHT** (or **Enter**); **Cancel/Back** saves and returns to Tools. Navigate with **UP/DOWN**, change a value with **LEFT/RIGHT** (or **Enter**); **Cancel/Back** saves and returns to Tools.

View File

@@ -73,9 +73,11 @@ public:
virtual void onSharedLocation(const uint8_t* pub_key, const char* name, virtual void onSharedLocation(const uint8_t* pub_key, const char* name,
int32_t lat_1e6, int32_t lon_1e6, int32_t lat_1e6, int32_t lon_1e6,
uint32_t ts, bool verified) {} uint32_t ts, bool verified) {}
// A contact was removed (companion app / CLI command). Lets UI state that // A contact is gone — removed explicitly (companion app / CLI command) or
// references contacts by pubkey (favourite slots, the Locator/Live Share // silently auto-evicted to make room when the contact table is full. Lets
// target) drop a reference that would otherwise dangle. Default no-op. // UI state that references contacts by pubkey (favourite slots, the
// Locator/Live Share target) drop a reference that would otherwise dangle.
// Default no-op.
virtual void onContactRemoved(const uint8_t* pub_key) {} virtual void onContactRemoved(const uint8_t* pub_key) {}
virtual void loop() = 0; virtual void loop() = 0;
}; };

View File

@@ -352,6 +352,7 @@ uint8_t MyMesh::getAutoAddMaxHops() const {
void MyMesh::onContactOverwrite(const uint8_t* pub_key) { void MyMesh::onContactOverwrite(const uint8_t* pub_key) {
_store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); // delete from storage _store->deleteBlobByKey(pub_key, PUB_KEY_SIZE); // delete from storage
if (_ui) _ui->onContactRemoved(pub_key); // same cleanup as an explicit CMD_REMOVE_CONTACT
if (_serial->isConnected()) { if (_serial->isConnected()) {
out_frame[0] = PUSH_CODE_CONTACT_DELETED; out_frame[0] = PUSH_CODE_CONTACT_DELETED;
memcpy(&out_frame[1], pub_key, PUB_KEY_SIZE); memcpy(&out_frame[1], pub_key, PUB_KEY_SIZE);