fix(ui): persist & restore display names; stop showing "Future" pre-clock-sync

Two related bugs in the conversation list:

1. Without GPS/NTP, Utilities::OS::time() returns uptime seconds — way
   smaller than any real unix-epoch message timestamp. The
   format_timestamp "diff < 0 → Future" branch then fired on every
   row. Add a sane-epoch threshold (2024-01-01) below which we render
   "?" instead, since "Future" is misleading when it just means
   "we don't know what time it is."

2. Identity::recall_app_data is in-memory only and lost on reboot. The
   conversation list always re-fell back to truncated hashes on cold
   start. Wire the three-tier resolution flow: live announce →
   MessageStore-persisted name → hash. When the live cache hits, write
   through to the persisted side via MessageStore::set_display_name so
   future cold boots get the name back immediately.

Pulls in microLXMF 5531a59 (MessageStore display-name cache impl).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
torlando-tech
2026-05-07 19:47:36 -04:00
co-authored by Claude Opus 4.7
parent ed0eebe1f0
commit 2eeb81e42f
@@ -232,7 +232,12 @@ void ConversationListScreen::refresh() {
ConversationItem item;
item.peer_hash = peer_hash;
// Try to get display name from app_data, fall back to hash
// Resolve display name in three tiers:
// 1. Live announce cache (Identity::recall_app_data) — fresh
// 2. MessageStore-persisted name — survives reboots
// 3. Truncated hash — last resort
// When (1) succeeds, also write through to the persisted cache
// so future cold boots get (2) immediately.
bool resolved = false;
Bytes app_data = Identity::recall_app_data(peer_hash);
if (app_data && app_data.size() > 0) {
@@ -240,13 +245,21 @@ void ConversationListScreen::refresh() {
if (display_name.length() > 0) {
item.peer_name = display_name;
resolved = true;
} else {
item.peer_name = truncate_hash(peer_hash);
if (_message_store) {
_message_store->set_display_name(
peer_hash, std::string(display_name.c_str()));
}
}
}
if (!resolved && _message_store) {
std::string cached = _message_store->get_display_name(peer_hash);
if (!cached.empty()) {
item.peer_name = String(cached.c_str());
resolved = true;
}
} else {
item.peer_name = truncate_hash(peer_hash);
}
if (!resolved) {
item.peer_name = truncate_hash(peer_hash);
_has_unresolved_names = true;
}
@@ -705,10 +718,25 @@ void ConversationListScreen::on_delete_confirmed(lv_event_t* event) {
String ConversationListScreen::format_timestamp(uint32_t timestamp) {
double now = Utilities::OS::time();
// If our local clock hasn't been set (no GPS lock yet, no NTP) it's
// running off uptime in seconds — way smaller than any real
// unix-epoch timestamp from a properly-clocked peer. The legacy
// "diff < 0 → Future" branch then mis-fires on every message,
// because every message looks like it's from the future.
//
// Threshold: 2024-01-01 UTC = 1704067200. Anything less than that
// means we don't have real time. Show "?" rather than confusing the
// user with "Future" on every row.
constexpr double SANE_EPOCH = 1704067200.0;
if (now < SANE_EPOCH) {
return "?"; // Local clock not yet synced
}
double diff = now - (double)timestamp;
if (diff < 0) {
return "Future"; // Clock not synced or future timestamp
return "Future"; // Genuine future timestamp (peer's clock is off)
} else if (diff < 60) {
return "Just now";
} else if (diff < 3600) {