mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 14:58:12 +00:00
feat: show last-heard time in detail view, fix disappearing contact edge case
- Detail view now shows "Seen: Xs/Xm/Xh ago" using ContactInfo.lastmod - Periodic detail refresh exits back to list view if the contact is no longer in the filtered list (e.g. removed from favourites) - Rearranged detail layout to fit 6 rows within 64px display Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,11 +18,12 @@ class NearbyScreen : public UIScreen {
|
||||
static const uint8_t FILTER_TYPES[FILTER_COUNT];
|
||||
|
||||
struct Entry {
|
||||
char name[32];
|
||||
int32_t lat_e6, lon_e6;
|
||||
float dist_km;
|
||||
uint8_t type;
|
||||
int contact_idx;
|
||||
char name[32];
|
||||
int32_t lat_e6, lon_e6;
|
||||
float dist_km;
|
||||
uint8_t type;
|
||||
int contact_idx;
|
||||
uint32_t lastmod; // our RTC timestamp of last received packet
|
||||
};
|
||||
|
||||
static const int MAX_NEARBY = 32;
|
||||
@@ -40,6 +41,10 @@ class NearbyScreen : public UIScreen {
|
||||
unsigned long _scan_started_ms;
|
||||
static const unsigned long SCAN_DURATION_MS = 4000UL;
|
||||
|
||||
// detail view periodic refresh
|
||||
unsigned long _detail_refresh_ms;
|
||||
static const unsigned long DETAIL_REFRESH_MS = 10000UL;
|
||||
|
||||
// context menu (list view)
|
||||
PopupMenu _ctx_menu;
|
||||
|
||||
@@ -69,6 +74,16 @@ class NearbyScreen : public UIScreen {
|
||||
return dirs[((deg + 22) % 360) / 45];
|
||||
}
|
||||
|
||||
static void fmtAge(char* buf, int n, uint32_t lastmod) {
|
||||
uint32_t now = rtc_clock.getCurrentTime();
|
||||
if (now < lastmod || lastmod == 0) { snprintf(buf, n, "unknown"); return; }
|
||||
uint32_t age = now - lastmod;
|
||||
if (age < 60) snprintf(buf, n, "%us ago", age);
|
||||
else if (age < 3600) snprintf(buf, n, "%um ago", age / 60);
|
||||
else if (age < 86400) snprintf(buf, n, "%uh ago", age / 3600);
|
||||
else snprintf(buf, n, ">1d ago");
|
||||
}
|
||||
|
||||
static void fmtDist(char* buf, int n, float km) {
|
||||
if (km < 1.0f) snprintf(buf, n, "%dm", (int)(km * 1000 + 0.5f));
|
||||
else if (km < 100.0f) snprintf(buf, n, "%.1fkm", km);
|
||||
@@ -123,6 +138,7 @@ class NearbyScreen : public UIScreen {
|
||||
: -1.0f;
|
||||
e.type = ci.type;
|
||||
e.contact_idx = i;
|
||||
e.lastmod = ci.lastmod;
|
||||
}
|
||||
|
||||
// sort by distance ascending; nodes without GPS (-1) go to the end
|
||||
@@ -165,6 +181,20 @@ public:
|
||||
refresh();
|
||||
}
|
||||
|
||||
// periodic refresh in detail view — preserve selected contact by idx
|
||||
if (_detail && millis() - _detail_refresh_ms >= DETAIL_REFRESH_MS) {
|
||||
int saved_contact_idx = (_sel < _count) ? _entries[_sel].contact_idx : -1;
|
||||
refresh();
|
||||
bool found = false;
|
||||
if (saved_contact_idx >= 0) {
|
||||
for (int i = 0; i < _count; i++) {
|
||||
if (_entries[i].contact_idx == saved_contact_idx) { _sel = i; found = true; break; }
|
||||
}
|
||||
}
|
||||
if (!found) _detail = false; // contact left the filtered list — return to list view
|
||||
_detail_refresh_ms = millis();
|
||||
}
|
||||
|
||||
// ── detail view ──────────────────────────────────────────────────────────
|
||||
if (_detail && _sel < _count) {
|
||||
const Entry& e = _entries[_sel];
|
||||
@@ -177,28 +207,34 @@ public:
|
||||
display.drawTextEllipsized(2, 1, display.width() - 4, filtered);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
// 6 rows in 54px (y=10..63): spacing 9px, start at y=11
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "Lat: %.5f", e.lat_e6 / 1e6);
|
||||
display.setCursor(2, 13); display.print(buf);
|
||||
display.setCursor(2, 11); display.print(buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "Lon: %.5f", e.lon_e6 / 1e6);
|
||||
display.setCursor(2, 22); display.print(buf);
|
||||
display.setCursor(2, 20); display.print(buf);
|
||||
|
||||
if (e.dist_km >= 0.0f) {
|
||||
char dist[12];
|
||||
fmtDist(dist, sizeof(dist), e.dist_km);
|
||||
int az = bearingDeg(_own_lat, _own_lon, e.lat_e6, e.lon_e6);
|
||||
snprintf(buf, sizeof(buf), "Dist: %s", dist);
|
||||
display.setCursor(2, 31); display.print(buf);
|
||||
display.setCursor(2, 29); display.print(buf);
|
||||
snprintf(buf, sizeof(buf), "Az: %dd (%s)", az, bearingCardinal(az));
|
||||
display.setCursor(2, 40); display.print(buf);
|
||||
display.setCursor(2, 38); display.print(buf);
|
||||
} else {
|
||||
display.setCursor(2, 31); display.print("Dist: no own GPS");
|
||||
display.setCursor(2, 40); display.print("Az: unknown");
|
||||
display.setCursor(2, 29); display.print("Dist: no own GPS");
|
||||
display.setCursor(2, 38); display.print("Az: unknown");
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "Type:%s", typeName(e.type));
|
||||
display.setCursor(2, 49); display.print(buf);
|
||||
display.setCursor(2, 47); display.print(buf);
|
||||
|
||||
char age[16];
|
||||
fmtAge(age, sizeof(age), e.lastmod);
|
||||
snprintf(buf, sizeof(buf), "Seen:%s", age);
|
||||
display.setCursor(2, 56); display.print(buf);
|
||||
|
||||
return 2000;
|
||||
}
|
||||
@@ -303,6 +339,7 @@ public:
|
||||
}
|
||||
if (c == KEY_ENTER && _count > 0) {
|
||||
_detail = true;
|
||||
_detail_refresh_ms = millis();
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_LEFT) {
|
||||
|
||||
Reference in New Issue
Block a user