mirror of
https://github.com/ratspeak/ratdeck.git
synced 2026-03-30 06:10:03 +00:00
Fix TCP messaging, LoRa status reversion, and message ordering (v1.5.8)
- TCPClientInterface overrides needs_transport_headers() so hops==1 packets get HEADER_2 wrapping through the hub - Track savedCounter in LXMFMessage and use direct filename lookup for status updates, fixing LoRa uptime-timestamp precision loss that caused SENT status to revert to QUEUED on reload - Remove timestamp-based re-sort in loadConversation() which broke chronological order when mixing LoRa (uptime) and TCP (epoch) timestamps; counter-based filenames already provide correct order
This commit is contained in:
@@ -30,7 +30,9 @@ void LXMFManager::loop() {
|
||||
|
||||
// Persist updated status to disk so reloads don't revert to QUEUED
|
||||
std::string peerHex = msg.destHash.toHex();
|
||||
if (_store) {
|
||||
if (_store && msg.savedCounter > 0) {
|
||||
_store->updateMessageStatusByCounter(peerHex, msg.savedCounter, false, msg.status);
|
||||
} else if (_store) {
|
||||
_store->updateMessageStatus(peerHex, msg.timestamp, false, msg.status);
|
||||
}
|
||||
|
||||
@@ -60,7 +62,8 @@ bool LXMFManager::sendMessage(const RNS::Bytes& destHash, const std::string& con
|
||||
if ((int)_outQueue.size() >= RATDECK_MAX_OUTQUEUE) { _outQueue.pop_front(); }
|
||||
_outQueue.push_back(msg);
|
||||
// Immediately save with QUEUED status so it appears in getMessages() right away
|
||||
if (_store) { _store->saveMessage(msg); }
|
||||
// Save the queue copy so savedCounter propagates back to the queued message
|
||||
if (_store) { _store->saveMessage(_outQueue.back()); }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ struct LXMFMessage {
|
||||
bool incoming = false;
|
||||
bool read = false;
|
||||
int retries = 0;
|
||||
uint32_t savedCounter = 0;
|
||||
RNS::Bytes messageId;
|
||||
|
||||
static std::vector<uint8_t> packContent(double timestamp, const std::string& content, const std::string& title);
|
||||
|
||||
@@ -246,7 +246,7 @@ void MessageStore::refreshConversations() {
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageStore::saveMessage(const LXMFMessage& msg) {
|
||||
bool MessageStore::saveMessage(LXMFMessage& msg) {
|
||||
if (!_flash) return false;
|
||||
|
||||
std::string peerHex = msg.incoming ?
|
||||
@@ -270,6 +270,7 @@ bool MessageStore::saveMessage(const LXMFMessage& msg) {
|
||||
|
||||
// Counter-based filename: unique, monotonic, sorts correctly
|
||||
uint32_t counter = _nextReceiveCounter++;
|
||||
msg.savedCounter = counter;
|
||||
char filename[64];
|
||||
snprintf(filename, sizeof(filename), "%013lu_%c.json",
|
||||
(unsigned long)counter, msg.incoming ? 'i' : 'o');
|
||||
@@ -393,15 +394,6 @@ std::vector<LXMFMessage> MessageStore::loadConversation(const std::string& peerH
|
||||
}
|
||||
}
|
||||
|
||||
// Sort chronologically; non-epoch timestamps (uptime-based) sort before epoch
|
||||
std::sort(messages.begin(), messages.end(),
|
||||
[](const LXMFMessage& a, const LXMFMessage& b) {
|
||||
bool aEpoch = a.timestamp > 1700000000;
|
||||
bool bEpoch = b.timestamp > 1700000000;
|
||||
if (aEpoch != bEpoch) return !aEpoch; // non-epoch sorts before epoch
|
||||
return a.timestamp < b.timestamp;
|
||||
});
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -598,6 +590,44 @@ bool MessageStore::updateMessageStatus(const std::string& peerHex, double timest
|
||||
return updated;
|
||||
}
|
||||
|
||||
bool MessageStore::updateMessageStatusByCounter(const std::string& peerHex, uint32_t counter, bool incoming, LXMFStatus newStatus) {
|
||||
if (counter == 0) return false; // Not saved yet
|
||||
char filename[64];
|
||||
snprintf(filename, sizeof(filename), "%013lu_%c.json",
|
||||
(unsigned long)counter, incoming ? 'i' : 'o');
|
||||
|
||||
auto readModifyWrite = [&](auto readFn, auto writeFn, const String& dir) -> bool {
|
||||
String path = dir + "/" + filename;
|
||||
String json = readFn(path.c_str());
|
||||
if (json.length() == 0) return false;
|
||||
JsonDocument doc;
|
||||
if (deserializeJson(doc, json)) return false;
|
||||
doc["status"] = (int)newStatus;
|
||||
String updated;
|
||||
serializeJson(doc, updated);
|
||||
writeFn(path.c_str(), updated);
|
||||
return true;
|
||||
};
|
||||
|
||||
bool updated = false;
|
||||
if (_sd && _sd->isReady()) {
|
||||
String sdDir = sdConversationDir(peerHex);
|
||||
updated = readModifyWrite(
|
||||
[&](const char* p) { return _sd->readString(p); },
|
||||
[&](const char* p, const String& d) { _sd->writeString(p, d); },
|
||||
sdDir);
|
||||
}
|
||||
if (_flash) {
|
||||
String dir = conversationDir(peerHex);
|
||||
bool flashUpdated = readModifyWrite(
|
||||
[this](const char* p) { return _flash->readString(p); },
|
||||
[this](const char* p, const String& d) { _flash->writeString(p, d); },
|
||||
dir);
|
||||
updated = updated || flashUpdated;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
void MessageStore::buildSummaries() {
|
||||
_summaries.clear();
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class MessageStore {
|
||||
public:
|
||||
bool begin(FlashStore* flash, SDStore* sd = nullptr);
|
||||
|
||||
bool saveMessage(const LXMFMessage& msg);
|
||||
bool saveMessage(LXMFMessage& msg);
|
||||
std::vector<LXMFMessage> loadConversation(const std::string& peerHex) const;
|
||||
const std::vector<std::string>& conversations() const { return _conversations; }
|
||||
void refreshConversations();
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
bool deleteConversation(const std::string& peerHex);
|
||||
void markConversationRead(const std::string& peerHex);
|
||||
bool updateMessageStatus(const std::string& peerHex, double timestamp, bool incoming, LXMFStatus newStatus);
|
||||
bool updateMessageStatusByCounter(const std::string& peerHex, uint32_t counter, bool incoming, LXMFStatus newStatus);
|
||||
|
||||
const ConversationSummary* getSummary(const std::string& peerHex) const;
|
||||
int totalUnreadCount() const;
|
||||
|
||||
@@ -13,6 +13,8 @@ public:
|
||||
void stop() override;
|
||||
void loop() override;
|
||||
|
||||
bool needs_transport_headers() const override { return true; }
|
||||
|
||||
virtual inline std::string toString() const override {
|
||||
return "TCPClient[" + _name + "]";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user