Stamp incoming messages with receiver time, radio-style freq editor

Timestamps: Incoming messages with invalid sender timestamps
(< Nov 2023 epoch) are now stamped with the receiver's local time
if available. Previously, messages from devices without NTP/GPS
showed no timestamp at all.

Frequency editor: Replace raw 9-digit Hz accumulator with a
radio-style digit cursor. Display shows NNN.NNN.NNN format with
bracket cursor on active digit (e.g., "920.[6]50.500"). Trackball
left/right moves cursor, digit keys replace at cursor and advance.
Esc restores original value. Matches amateur radio transceiver UX.
This commit is contained in:
DeFiDude
2026-03-28 16:25:50 -06:00
parent f41e4c0363
commit d81921357b
3 changed files with 100 additions and 1 deletions

View File

@@ -309,6 +309,11 @@ void LXMFManager::processIncoming(const uint8_t* data, size_t len, const RNS::By
if (destHash.size() > 0) {
msg.destHash = destHash;
}
// Stamp with receiver's local time if sender's timestamp is missing/invalid
if (msg.timestamp < 1700000000) {
time_t now = time(nullptr);
if (now > 1700000000) msg.timestamp = (double)now;
}
Serial.printf("[LXMF] Message from %s (%d bytes) content_len=%d\n",
msg.sourceHash.toHex().substr(0, 8).c_str(), (int)len, (int)msg.content.size());
if (_store) { _store->saveMessage(msg); }

View File

@@ -810,7 +810,10 @@ void LvSettingsScreen::rebuildItemList() {
String valStr;
uint32_t valColor = Theme::PRIMARY;
if (_editing && selected) {
if (_freqEditing && selected) {
valStr = String("< ") + freqFormatWithCursor() + " >";
valColor = Theme::WARNING_CLR;
} else if (_editing && selected) {
if (item.type == SettingType::ENUM_CHOICE && !item.enumLabels.empty()) {
int vi = constrain(_editValue, 0, (int)item.enumLabels.size() - 1);
valStr = String("< ") + item.enumLabels[vi] + " >";
@@ -1055,6 +1058,41 @@ bool LvSettingsScreen::handleKey(const KeyEvent& event) {
return true;
}
// Frequency digit-cursor edit mode
if (_freqEditing) {
if (event.left) {
if (_freqCursor > 0) _freqCursor--;
rebuildItemList(); return true;
}
if (event.right) {
if (_freqCursor < 8) _freqCursor++;
rebuildItemList(); return true;
}
if (event.character >= '0' && event.character <= '9') {
_freqDigits[_freqCursor] = event.character - '0';
_editValue = freqRecompose();
if (_freqCursor < 8) _freqCursor++;
rebuildItemList(); return true;
}
if (event.enter || event.character == '\n' || event.character == '\r') {
auto& item = _items[_selectedIdx];
_editValue = freqRecompose();
if (item.setter) item.setter(_editValue);
_freqEditing = false; _editing = false;
applyAndSave(); rebuildItemList(); return true;
}
if (event.del || event.character == 8) {
if (_freqCursor > 0) _freqCursor--;
rebuildItemList(); return true;
}
if (event.character == 0x1B) {
_editValue = _freqOriginal;
_freqEditing = false; _editing = false;
rebuildItemList(); return true;
}
return true; // Consume all keys in freq mode
}
// Value edit mode
if (_editing) {
auto& item = _items[_selectedIdx];
@@ -1148,6 +1186,15 @@ bool LvSettingsScreen::handleKey(const KeyEvent& event) {
if (item.setter) item.setter(val ? 0 : 1);
applyAndSave();
rebuildItemList();
} else if (strcmp(item.label, "Frequency") == 0 && item.type == SettingType::INTEGER) {
// Radio-style digit cursor editor for frequency
_editing = true;
_editValue = item.getter ? item.getter() : 0;
_freqOriginal = _editValue;
freqDecompose(_editValue);
_freqCursor = 0;
_freqEditing = true;
rebuildItemList();
} else {
_editing = true;
_numericTyping = false;
@@ -1230,6 +1277,44 @@ bool LvSettingsScreen::tcpSettingsChanged() const {
return curHost != _tcpSnapHost || curPort != _tcpSnapPort;
}
// --- Frequency digit-cursor editor helpers ---
void LvSettingsScreen::freqDecompose(int value) {
// Decompose Hz value into 9 individual digits (left-padded with zeros)
for (int i = 8; i >= 0; i--) {
_freqDigits[i] = value % 10;
value /= 10;
}
}
int LvSettingsScreen::freqRecompose() const {
int val = 0;
for (int i = 0; i < 9; i++) val = val * 10 + _freqDigits[i];
return val;
}
String LvSettingsScreen::freqFormatWithCursor() const {
// Format as "NNN.NNN.NNN" with brackets around cursor digit
char buf[24];
char digits[9];
for (int i = 0; i < 9; i++) digits[i] = '0' + _freqDigits[i];
// Build string with cursor brackets: e.g., "920.[6]50.500"
int pos = 0;
for (int i = 0; i < 9; i++) {
if (i == 3 || i == 6) buf[pos++] = '.';
if (i == _freqCursor) {
buf[pos++] = '[';
buf[pos++] = digits[i];
buf[pos++] = ']';
} else {
buf[pos++] = digits[i];
}
}
buf[pos] = '\0';
return String(buf);
}
void LvSettingsScreen::applyAndSave() {
if (!_cfg) return;
auto& s = _cfg->settings();

View File

@@ -135,6 +135,15 @@ private:
bool _confirmingReset = false;
bool _confirmingDevMode = false;
// Frequency digit-cursor editor (radio-style)
bool _freqEditing = false;
int _freqCursor = 0; // 0-8, active digit position
int _freqDigits[9] = {}; // Individual digits of Hz frequency
int _freqOriginal = 0; // Original value for Esc cancel
void freqDecompose(int value);
int freqRecompose() const;
String freqFormatWithCursor() const;
// WiFi picker
std::vector<WiFiInterface::ScanResult> _wifiResults;
int _wifiPickerIdx = 0;