mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-08-03 18:56:15 +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:
co-authored by
Claude Sonnet 4.6
parent
404cb6956d
commit
f434852706
@@ -18,11 +18,12 @@ class NearbyScreen : public UIScreen {
|
|||||||
static const uint8_t FILTER_TYPES[FILTER_COUNT];
|
static const uint8_t FILTER_TYPES[FILTER_COUNT];
|
||||||
|
|
||||||
struct Entry {
|
struct Entry {
|
||||||
char name[32];
|
char name[32];
|
||||||
int32_t lat_e6, lon_e6;
|
int32_t lat_e6, lon_e6;
|
||||||
float dist_km;
|
float dist_km;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
int contact_idx;
|
int contact_idx;
|
||||||
|
uint32_t lastmod; // our RTC timestamp of last received packet
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int MAX_NEARBY = 32;
|
static const int MAX_NEARBY = 32;
|
||||||
@@ -40,6 +41,10 @@ class NearbyScreen : public UIScreen {
|
|||||||
unsigned long _scan_started_ms;
|
unsigned long _scan_started_ms;
|
||||||
static const unsigned long SCAN_DURATION_MS = 4000UL;
|
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)
|
// context menu (list view)
|
||||||
PopupMenu _ctx_menu;
|
PopupMenu _ctx_menu;
|
||||||
|
|
||||||
@@ -69,6 +74,16 @@ class NearbyScreen : public UIScreen {
|
|||||||
return dirs[((deg + 22) % 360) / 45];
|
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) {
|
static void fmtDist(char* buf, int n, float km) {
|
||||||
if (km < 1.0f) snprintf(buf, n, "%dm", (int)(km * 1000 + 0.5f));
|
if (km < 1.0f) snprintf(buf, n, "%dm", (int)(km * 1000 + 0.5f));
|
||||||
else if (km < 100.0f) snprintf(buf, n, "%.1fkm", km);
|
else if (km < 100.0f) snprintf(buf, n, "%.1fkm", km);
|
||||||
@@ -123,6 +138,7 @@ class NearbyScreen : public UIScreen {
|
|||||||
: -1.0f;
|
: -1.0f;
|
||||||
e.type = ci.type;
|
e.type = ci.type;
|
||||||
e.contact_idx = i;
|
e.contact_idx = i;
|
||||||
|
e.lastmod = ci.lastmod;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by distance ascending; nodes without GPS (-1) go to the end
|
// sort by distance ascending; nodes without GPS (-1) go to the end
|
||||||
@@ -165,6 +181,20 @@ public:
|
|||||||
refresh();
|
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 ──────────────────────────────────────────────────────────
|
// ── detail view ──────────────────────────────────────────────────────────
|
||||||
if (_detail && _sel < _count) {
|
if (_detail && _sel < _count) {
|
||||||
const Entry& e = _entries[_sel];
|
const Entry& e = _entries[_sel];
|
||||||
@@ -177,28 +207,34 @@ public:
|
|||||||
display.drawTextEllipsized(2, 1, display.width() - 4, filtered);
|
display.drawTextEllipsized(2, 1, display.width() - 4, filtered);
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
|
|
||||||
|
// 6 rows in 54px (y=10..63): spacing 9px, start at y=11
|
||||||
char buf[32];
|
char buf[32];
|
||||||
snprintf(buf, sizeof(buf), "Lat: %.5f", e.lat_e6 / 1e6);
|
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);
|
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) {
|
if (e.dist_km >= 0.0f) {
|
||||||
char dist[12];
|
char dist[12];
|
||||||
fmtDist(dist, sizeof(dist), e.dist_km);
|
fmtDist(dist, sizeof(dist), e.dist_km);
|
||||||
int az = bearingDeg(_own_lat, _own_lon, e.lat_e6, e.lon_e6);
|
int az = bearingDeg(_own_lat, _own_lon, e.lat_e6, e.lon_e6);
|
||||||
snprintf(buf, sizeof(buf), "Dist: %s", dist);
|
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));
|
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 {
|
} else {
|
||||||
display.setCursor(2, 31); display.print("Dist: no own GPS");
|
display.setCursor(2, 29); display.print("Dist: no own GPS");
|
||||||
display.setCursor(2, 40); display.print("Az: unknown");
|
display.setCursor(2, 38); display.print("Az: unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "Type:%s", typeName(e.type));
|
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;
|
return 2000;
|
||||||
}
|
}
|
||||||
@@ -303,6 +339,7 @@ public:
|
|||||||
}
|
}
|
||||||
if (c == KEY_ENTER && _count > 0) {
|
if (c == KEY_ENTER && _count > 0) {
|
||||||
_detail = true;
|
_detail = true;
|
||||||
|
_detail_refresh_ms = millis();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c == KEY_LEFT) {
|
if (c == KEY_LEFT) {
|
||||||
|
|||||||
Reference in New Issue
Block a user