diff --git a/src/reticulum/LXMFManager.cpp b/src/reticulum/LXMFManager.cpp index 059bb86..3d32fc0 100644 --- a/src/reticulum/LXMFManager.cpp +++ b/src/reticulum/LXMFManager.cpp @@ -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; } diff --git a/src/reticulum/LXMFMessage.h b/src/reticulum/LXMFMessage.h index 4837b17..fad43ae 100644 --- a/src/reticulum/LXMFMessage.h +++ b/src/reticulum/LXMFMessage.h @@ -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 packContent(double timestamp, const std::string& content, const std::string& title); diff --git a/src/storage/MessageStore.cpp b/src/storage/MessageStore.cpp index e10fa48..8754818 100644 --- a/src/storage/MessageStore.cpp +++ b/src/storage/MessageStore.cpp @@ -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 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(); diff --git a/src/storage/MessageStore.h b/src/storage/MessageStore.h index fcc2179..bb91949 100644 --- a/src/storage/MessageStore.h +++ b/src/storage/MessageStore.h @@ -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 loadConversation(const std::string& peerHex) const; const std::vector& 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; diff --git a/src/transport/TCPClientInterface.h b/src/transport/TCPClientInterface.h index 7e13bf9..7c92075 100644 --- a/src/transport/TCPClientInterface.h +++ b/src/transport/TCPClientInterface.h @@ -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 + "]"; }