mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-22 02:32:23 +00:00
More optimization
This commit is contained in:
@@ -9,6 +9,48 @@
|
|||||||
#include "graphics/draw/MessageRenderer.h"
|
#include "graphics/draw/MessageRenderer.h"
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
|
|
||||||
|
#ifndef MESSAGE_TEXT_POOL_SIZE
|
||||||
|
#define MESSAGE_TEXT_POOL_SIZE (MAX_MESSAGES_SAVED * MAX_MESSAGE_SIZE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Global message text pool and state
|
||||||
|
static char g_messagePool[MESSAGE_TEXT_POOL_SIZE];
|
||||||
|
static size_t g_poolWritePos = 0;
|
||||||
|
|
||||||
|
// Reset pool (called on boot or clear)
|
||||||
|
static inline void resetMessagePool()
|
||||||
|
{
|
||||||
|
g_poolWritePos = 0;
|
||||||
|
memset(g_messagePool, 0, sizeof(g_messagePool));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate text in pool and return offset
|
||||||
|
// If not enough space remains, wrap around (ring buffer style)
|
||||||
|
static inline uint16_t storeTextInPool(const char *src, size_t len)
|
||||||
|
{
|
||||||
|
if (len >= MAX_MESSAGE_SIZE)
|
||||||
|
len = MAX_MESSAGE_SIZE - 1;
|
||||||
|
|
||||||
|
// Wrap pool if out of space
|
||||||
|
if (g_poolWritePos + len + 1 >= MESSAGE_TEXT_POOL_SIZE) {
|
||||||
|
g_poolWritePos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t offset = g_poolWritePos;
|
||||||
|
memcpy(&g_messagePool[g_poolWritePos], src, len);
|
||||||
|
g_messagePool[g_poolWritePos + len] = '\0';
|
||||||
|
g_poolWritePos += (len + 1);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve a const pointer to message text by offset
|
||||||
|
static inline const char *getTextFromPool(uint16_t offset)
|
||||||
|
{
|
||||||
|
if (offset >= MESSAGE_TEXT_POOL_SIZE)
|
||||||
|
return "";
|
||||||
|
return &g_messagePool[offset];
|
||||||
|
}
|
||||||
|
|
||||||
// Helper: assign a timestamp (RTC if available, else boot-relative)
|
// Helper: assign a timestamp (RTC if available, else boot-relative)
|
||||||
static inline void assignTimestamp(StoredMessage &sm)
|
static inline void assignTimestamp(StoredMessage &sm)
|
||||||
{
|
{
|
||||||
@@ -40,6 +82,7 @@ template <typename T> static inline void pushWithLimit(std::deque<T> &queue, T &
|
|||||||
MessageStore::MessageStore(const std::string &label)
|
MessageStore::MessageStore(const std::string &label)
|
||||||
{
|
{
|
||||||
filename = "/Messages_" + label + ".msgs";
|
filename = "/Messages_" + label + ".msgs";
|
||||||
|
resetMessagePool(); // initialize text pool on boot
|
||||||
}
|
}
|
||||||
|
|
||||||
// Live message handling (RAM only)
|
// Live message handling (RAM only)
|
||||||
@@ -56,24 +99,42 @@ void MessageStore::addLiveMessage(const StoredMessage &msg)
|
|||||||
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
|
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
|
||||||
{
|
{
|
||||||
StoredMessage sm;
|
StoredMessage sm;
|
||||||
assignTimestamp(sm); // set timestamp (RTC or boot-relative)
|
assignTimestamp(sm);
|
||||||
sm.channelIndex = packet.channel;
|
sm.channelIndex = packet.channel;
|
||||||
strncpy(sm.text, reinterpret_cast<const char *>(packet.decoded.payload.bytes), MAX_MESSAGE_SIZE - 1);
|
|
||||||
sm.text[MAX_MESSAGE_SIZE - 1] = '\0';
|
|
||||||
|
|
||||||
|
const char *payload = reinterpret_cast<const char *>(packet.decoded.payload.bytes);
|
||||||
|
size_t len = strnlen(payload, MAX_MESSAGE_SIZE - 1);
|
||||||
|
sm.textOffset = storeTextInPool(payload, len);
|
||||||
|
sm.textLength = len;
|
||||||
|
|
||||||
|
uint32_t localNode = nodeDB->getNodeNum();
|
||||||
|
sm.sender = (packet.from == 0) ? localNode : packet.from;
|
||||||
|
sm.dest = packet.decoded.dest;
|
||||||
|
|
||||||
|
// DM detection: use decoded.dest if valid, otherwise fallback to header 'to'
|
||||||
|
bool isDM = false;
|
||||||
|
uint32_t actualDest = sm.dest;
|
||||||
|
|
||||||
|
if (actualDest == 0 || actualDest == 0xffffffff) {
|
||||||
|
actualDest = packet.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actualDest != 0 && actualDest != NODENUM_BROADCAST && actualDest == localNode) {
|
||||||
|
isDM = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incoming vs outgoing classification
|
||||||
if (packet.from == 0) {
|
if (packet.from == 0) {
|
||||||
// Outgoing (phone-originated)
|
// Sent by us
|
||||||
sm.sender = nodeDB->getNodeNum();
|
sm.type = isDM ? MessageType::DM_TO_US : MessageType::BROADCAST;
|
||||||
sm.dest = (packet.decoded.dest == 0) ? NODENUM_BROADCAST : packet.decoded.dest;
|
|
||||||
sm.type = (sm.dest == NODENUM_BROADCAST) ? MessageType::BROADCAST : MessageType::DM_TO_US;
|
|
||||||
sm.ackStatus = AckStatus::NONE;
|
sm.ackStatus = AckStatus::NONE;
|
||||||
} else {
|
} else {
|
||||||
// Incoming
|
// Received from another node
|
||||||
sm.sender = packet.from;
|
if (isDM) {
|
||||||
sm.dest = packet.decoded.dest;
|
sm.type = MessageType::DM_TO_US;
|
||||||
sm.type = (sm.dest == NODENUM_BROADCAST) ? MessageType::BROADCAST
|
} else {
|
||||||
: (sm.dest == nodeDB->getNodeNum()) ? MessageType::DM_TO_US
|
sm.type = MessageType::BROADCAST;
|
||||||
: MessageType::BROADCAST;
|
}
|
||||||
sm.ackStatus = AckStatus::ACKED;
|
sm.ackStatus = AckStatus::ACKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,8 +152,8 @@ void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const st
|
|||||||
|
|
||||||
sm.sender = sender;
|
sm.sender = sender;
|
||||||
sm.channelIndex = channelIndex;
|
sm.channelIndex = channelIndex;
|
||||||
strncpy(sm.text, text.c_str(), MAX_MESSAGE_SIZE - 1);
|
sm.textOffset = storeTextInPool(text.c_str(), text.size());
|
||||||
sm.text[MAX_MESSAGE_SIZE - 1] = '\0';
|
sm.textLength = text.size();
|
||||||
|
|
||||||
// Default manual adds to broadcast
|
// Default manual adds to broadcast
|
||||||
sm.dest = NODENUM_BROADCAST;
|
sm.dest = NODENUM_BROADCAST;
|
||||||
@@ -106,10 +167,7 @@ void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const st
|
|||||||
|
|
||||||
#if ENABLE_MESSAGE_PERSISTENCE
|
#if ENABLE_MESSAGE_PERSISTENCE
|
||||||
|
|
||||||
// Use a compile-time constant so the array bound can be used in the struct
|
// Compact, fixed-size on-flash representation using offset + length
|
||||||
static constexpr size_t TEXT_LEN = MAX_MESSAGE_SIZE;
|
|
||||||
|
|
||||||
// Compact, fixed-size on-flash representation
|
|
||||||
struct __attribute__((packed)) StoredMessageRecord {
|
struct __attribute__((packed)) StoredMessageRecord {
|
||||||
uint32_t timestamp;
|
uint32_t timestamp;
|
||||||
uint32_t sender;
|
uint32_t sender;
|
||||||
@@ -117,7 +175,9 @@ struct __attribute__((packed)) StoredMessageRecord {
|
|||||||
uint32_t dest;
|
uint32_t dest;
|
||||||
uint8_t isBootRelative;
|
uint8_t isBootRelative;
|
||||||
uint8_t ackStatus; // static_cast<uint8_t>(AckStatus)
|
uint8_t ackStatus; // static_cast<uint8_t>(AckStatus)
|
||||||
char text[TEXT_LEN]; // null-terminated
|
uint8_t type; // static_cast<uint8_t>(MessageType)
|
||||||
|
uint16_t textLength; // message length
|
||||||
|
char text[MAX_MESSAGE_SIZE]; // <-- store actual text here
|
||||||
};
|
};
|
||||||
|
|
||||||
// Serialize one StoredMessage to flash
|
// Serialize one StoredMessage to flash
|
||||||
@@ -130,9 +190,14 @@ static inline void writeMessageRecord(SafeFile &f, const StoredMessage &m)
|
|||||||
rec.dest = m.dest;
|
rec.dest = m.dest;
|
||||||
rec.isBootRelative = m.isBootRelative;
|
rec.isBootRelative = m.isBootRelative;
|
||||||
rec.ackStatus = static_cast<uint8_t>(m.ackStatus);
|
rec.ackStatus = static_cast<uint8_t>(m.ackStatus);
|
||||||
|
rec.type = static_cast<uint8_t>(m.type);
|
||||||
|
rec.textLength = m.textLength;
|
||||||
|
|
||||||
|
// Copy the actual text into the record from RAM pool
|
||||||
|
const char *txt = getTextFromPool(m.textOffset);
|
||||||
|
strncpy(rec.text, txt, MAX_MESSAGE_SIZE - 1);
|
||||||
|
rec.text[MAX_MESSAGE_SIZE - 1] = '\0';
|
||||||
|
|
||||||
strncpy(rec.text, m.text, TEXT_LEN - 1);
|
|
||||||
rec.text[TEXT_LEN - 1] = '\0';
|
|
||||||
f.write(reinterpret_cast<const uint8_t *>(&rec), sizeof(rec));
|
f.write(reinterpret_cast<const uint8_t *>(&rec), sizeof(rec));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,9 +214,13 @@ static inline bool readMessageRecord(File &f, StoredMessage &m)
|
|||||||
m.dest = rec.dest;
|
m.dest = rec.dest;
|
||||||
m.isBootRelative = rec.isBootRelative;
|
m.isBootRelative = rec.isBootRelative;
|
||||||
m.ackStatus = static_cast<AckStatus>(rec.ackStatus);
|
m.ackStatus = static_cast<AckStatus>(rec.ackStatus);
|
||||||
strncpy(m.text, rec.text, MAX_MESSAGE_SIZE - 1);
|
m.type = static_cast<MessageType>(rec.type);
|
||||||
m.text[MAX_MESSAGE_SIZE - 1] = '\0';
|
m.textLength = rec.textLength;
|
||||||
m.type = (m.dest == NODENUM_BROADCAST) ? MessageType::BROADCAST : MessageType::DM_TO_US;
|
|
||||||
|
// 💡 Re-store text into pool and update offset
|
||||||
|
m.textLength = strnlen(rec.text, MAX_MESSAGE_SIZE - 1);
|
||||||
|
m.textOffset = storeTextInPool(rec.text, m.textLength);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +252,8 @@ void MessageStore::saveToFlash()
|
|||||||
void MessageStore::loadFromFlash()
|
void MessageStore::loadFromFlash()
|
||||||
{
|
{
|
||||||
liveMessages.clear();
|
liveMessages.clear();
|
||||||
|
resetMessagePool(); // reset pool when loading
|
||||||
|
|
||||||
#ifdef FSCom
|
#ifdef FSCom
|
||||||
concurrency::LockGuard guard(spiLock);
|
concurrency::LockGuard guard(spiLock);
|
||||||
|
|
||||||
@@ -219,6 +290,7 @@ void MessageStore::loadFromFlash() {}
|
|||||||
void MessageStore::clearAllMessages()
|
void MessageStore::clearAllMessages()
|
||||||
{
|
{
|
||||||
liveMessages.clear();
|
liveMessages.clear();
|
||||||
|
resetMessagePool();
|
||||||
|
|
||||||
#ifdef FSCom
|
#ifdef FSCom
|
||||||
SafeFile f(filename.c_str(), false);
|
SafeFile f(filename.c_str(), false);
|
||||||
@@ -266,7 +338,7 @@ void MessageStore::dismissOldestMessageInChannel(uint8_t channel)
|
|||||||
saveToFlash();
|
saveToFlash();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismiss oldest message in a direct conversation with a peer
|
// Dismiss oldest message in a direct chat with a node
|
||||||
void MessageStore::dismissOldestMessageWithPeer(uint32_t peer)
|
void MessageStore::dismissOldestMessageWithPeer(uint32_t peer)
|
||||||
{
|
{
|
||||||
auto pred = [peer](const StoredMessage &m) {
|
auto pred = [peer](const StoredMessage &m) {
|
||||||
@@ -279,7 +351,6 @@ void MessageStore::dismissOldestMessageWithPeer(uint32_t peer)
|
|||||||
saveToFlash();
|
saveToFlash();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper filters for future use
|
|
||||||
std::deque<StoredMessage> MessageStore::getChannelMessages(uint8_t channel) const
|
std::deque<StoredMessage> MessageStore::getChannelMessages(uint8_t channel) const
|
||||||
{
|
{
|
||||||
std::deque<StoredMessage> result;
|
std::deque<StoredMessage> result;
|
||||||
@@ -320,12 +391,23 @@ void MessageStore::upgradeBootRelativeTimestamps()
|
|||||||
m.timestamp += bootOffset;
|
m.timestamp += bootOffset;
|
||||||
m.isBootRelative = false;
|
m.isBootRelative = false;
|
||||||
}
|
}
|
||||||
// else: persisted from old boot → stays ??? forever
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fix(liveMessages);
|
fix(liveMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *MessageStore::getText(const StoredMessage &msg)
|
||||||
|
{
|
||||||
|
// Wrapper around the internal helper
|
||||||
|
return getTextFromPool(msg.textOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t MessageStore::storeText(const char *src, size_t len)
|
||||||
|
{
|
||||||
|
// Wrapper around the internal helper
|
||||||
|
return storeTextInPool(src, len);
|
||||||
|
}
|
||||||
|
|
||||||
// Global definition
|
// Global definition
|
||||||
MessageStore messageStore("default");
|
MessageStore messageStore("default");
|
||||||
#endif
|
#endif
|
||||||
@@ -27,10 +27,16 @@
|
|||||||
// Internal alias used everywhere in code – do NOT redefine elsewhere.
|
// Internal alias used everywhere in code – do NOT redefine elsewhere.
|
||||||
#define MAX_MESSAGES_SAVED MESSAGE_HISTORY_LIMIT
|
#define MAX_MESSAGES_SAVED MESSAGE_HISTORY_LIMIT
|
||||||
|
|
||||||
// Maximum text payload size per message in bytes (fixed).
|
// Maximum text payload size per message in bytes.
|
||||||
// All messages use the same size to simplify memory handling and avoid dynamic allocations.
|
// This still defines the max message length, but we no longer reserve this space per message.
|
||||||
#define MAX_MESSAGE_SIZE 220
|
#define MAX_MESSAGE_SIZE 220
|
||||||
|
|
||||||
|
// Total shared text pool size for all messages combined.
|
||||||
|
// The text pool is RAM-only. Text is re-stored from flash into the pool on boot.
|
||||||
|
#ifndef MESSAGE_TEXT_POOL_SIZE
|
||||||
|
#define MESSAGE_TEXT_POOL_SIZE (MAX_MESSAGES_SAVED * MAX_MESSAGE_SIZE)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Explicit message classification
|
// Explicit message classification
|
||||||
enum class MessageType : uint8_t {
|
enum class MessageType : uint8_t {
|
||||||
BROADCAST = 0, // broadcast message
|
BROADCAST = 0, // broadcast message
|
||||||
@@ -46,25 +52,23 @@ enum class AckStatus : uint8_t {
|
|||||||
RELAYED = 4 // got an ACK from relay, not destination
|
RELAYED = 4 // got an ACK from relay, not destination
|
||||||
};
|
};
|
||||||
|
|
||||||
// A single stored message in RAM and/or flash
|
|
||||||
struct StoredMessage {
|
struct StoredMessage {
|
||||||
uint32_t timestamp; // When message was created (secs since boot or RTC)
|
uint32_t timestamp; // When message was created (secs since boot or RTC)
|
||||||
uint32_t sender; // NodeNum of sender
|
uint32_t sender; // NodeNum of sender
|
||||||
uint8_t channelIndex; // Channel index used
|
uint8_t channelIndex; // Channel index used
|
||||||
char text[MAX_MESSAGE_SIZE]; // Fixed-size buffer for message text (null-terminated)
|
|
||||||
|
|
||||||
uint32_t dest; // Destination node (broadcast or direct)
|
uint32_t dest; // Destination node (broadcast or direct)
|
||||||
|
|
||||||
MessageType type; // Derived from dest (explicit classification)
|
MessageType type; // Derived from dest (explicit classification)
|
||||||
|
|
||||||
bool isBootRelative; // true = millis()/1000 fallback; false = epoch/RTC absolute
|
bool isBootRelative; // true = millis()/1000 fallback; false = epoch/RTC absolute
|
||||||
|
|
||||||
AckStatus ackStatus; // Delivery status (only meaningful for our own sent messages)
|
AckStatus ackStatus; // Delivery status (only meaningful for our own sent messages)
|
||||||
|
|
||||||
|
// Text storage metadata — rebuilt from flash at boot
|
||||||
|
uint16_t textOffset; // Offset into global text pool (valid only after loadFromFlash())
|
||||||
|
uint16_t textLength; // Length of text in bytes
|
||||||
|
|
||||||
// Default constructor initializes all fields safely
|
// Default constructor initializes all fields safely
|
||||||
StoredMessage()
|
StoredMessage()
|
||||||
: timestamp(0), sender(0), channelIndex(0), text(""), dest(0xffffffff), type(MessageType::BROADCAST),
|
: timestamp(0), sender(0), channelIndex(0), dest(0xffffffff), type(MessageType::BROADCAST), isBootRelative(false),
|
||||||
isBootRelative(false), ackStatus(AckStatus::NONE) // start as NONE (waiting, no symbol)
|
ackStatus(AckStatus::NONE), textOffset(0), textLength(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -87,7 +91,7 @@ class MessageStore
|
|||||||
void saveToFlash(); // Save messages to flash
|
void saveToFlash(); // Save messages to flash
|
||||||
void loadFromFlash(); // Load messages from flash
|
void loadFromFlash(); // Load messages from flash
|
||||||
|
|
||||||
// Clear all messages (RAM + persisted queue)
|
// Clear all messages (RAM + persisted queue + text pool)
|
||||||
void clearAllMessages();
|
void clearAllMessages();
|
||||||
|
|
||||||
// Dismiss helpers
|
// Dismiss helpers
|
||||||
@@ -107,6 +111,15 @@ class MessageStore
|
|||||||
// Upgrade boot-relative timestamps once RTC is valid
|
// Upgrade boot-relative timestamps once RTC is valid
|
||||||
void upgradeBootRelativeTimestamps();
|
void upgradeBootRelativeTimestamps();
|
||||||
|
|
||||||
|
// Retrieve the C-string text for a stored message
|
||||||
|
static const char *getText(const StoredMessage &msg);
|
||||||
|
|
||||||
|
// Allocate text into pool (used by sender-side code)
|
||||||
|
static uint16_t storeText(const char *src, size_t len);
|
||||||
|
|
||||||
|
// Used when loading from flash to rebuild the text pool
|
||||||
|
static uint16_t rebuildTextFromFlash(const char *src, size_t len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::deque<StoredMessage> liveMessages; // Single in-RAM message buffer (also used for persistence)
|
std::deque<StoredMessage> liveMessages; // Single in-RAM message buffer (also used for persistence)
|
||||||
std::string filename; // Flash filename for persistence
|
std::string filename; // Flash filename for persistence
|
||||||
|
|||||||
@@ -481,9 +481,6 @@ void menuHandler::messageResponseMenu()
|
|||||||
} else if (selected == DismissAll) {
|
} else if (selected == DismissAll) {
|
||||||
messageStore.clearAllMessages();
|
messageStore.clearAllMessages();
|
||||||
graphics::MessageRenderer::clearThreadRegistries();
|
graphics::MessageRenderer::clearThreadRegistries();
|
||||||
|
|
||||||
// Reset back to "View All"
|
|
||||||
graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::ALL);
|
|
||||||
} else if (selected == DismissOldest) {
|
} else if (selected == DismissOldest) {
|
||||||
auto mode = graphics::MessageRenderer::getThreadMode();
|
auto mode = graphics::MessageRenderer::getThreadMode();
|
||||||
int ch = graphics::MessageRenderer::getThreadChannel();
|
int ch = graphics::MessageRenderer::getThreadChannel();
|
||||||
|
|||||||
@@ -416,6 +416,14 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filtered.empty()) {
|
if (filtered.empty()) {
|
||||||
|
// If current conversation is empty go back to ALL view
|
||||||
|
if (currentMode != ThreadMode::ALL) {
|
||||||
|
setThreadMode(ThreadMode::ALL);
|
||||||
|
resetScrollState();
|
||||||
|
return; // Next draw will rerun in ALL mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still in ALL mode and no messages at all → show placeholder
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
didReset = false;
|
didReset = false;
|
||||||
const char *messageString = "No messages";
|
const char *messageString = "No messages";
|
||||||
@@ -528,8 +536,9 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
isHeader.push_back(true);
|
isHeader.push_back(true);
|
||||||
ackForLine.push_back(m.ackStatus);
|
ackForLine.push_back(m.ackStatus);
|
||||||
|
|
||||||
// Split message text into wrapped lines
|
const char *msgText = MessageStore::getText(m);
|
||||||
std::vector<std::string> wrapped = generateLines(display, "", m.text, textWidth);
|
|
||||||
|
std::vector<std::string> wrapped = generateLines(display, "", msgText, textWidth);
|
||||||
for (auto &ln : wrapped) {
|
for (auto &ln : wrapped) {
|
||||||
allLines.push_back(ln);
|
allLines.push_back(ln);
|
||||||
isMine.push_back(mine);
|
isMine.push_back(mine);
|
||||||
@@ -880,8 +889,11 @@ void handleNewMessage(const StoredMessage &sm, const meshtastic_MeshPacket &pack
|
|||||||
screen->showSimpleBanner(banner, inThread ? 1000 : 3000);
|
screen->showSimpleBanner(banner, inThread ? 1000 : 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always focus into the correct conversation thread when a message arrives
|
// Always focus into the correct conversation thread when a message with real text arrives
|
||||||
|
const char *msgText = MessageStore::getText(sm);
|
||||||
|
if (msgText && msgText[0] != '\0') {
|
||||||
setThreadFor(sm, packet);
|
setThreadFor(sm, packet);
|
||||||
|
}
|
||||||
|
|
||||||
// Reset scroll for a clean start
|
// Reset scroll for a clean start
|
||||||
resetScrollState();
|
resetScrollState();
|
||||||
@@ -889,10 +901,12 @@ void handleNewMessage(const StoredMessage &sm, const meshtastic_MeshPacket &pack
|
|||||||
|
|
||||||
void setThreadFor(const StoredMessage &sm, const meshtastic_MeshPacket &packet)
|
void setThreadFor(const StoredMessage &sm, const meshtastic_MeshPacket &packet)
|
||||||
{
|
{
|
||||||
if (sm.type == MessageType::BROADCAST) {
|
if (sm.dest == NODENUM_BROADCAST || sm.type == MessageType::BROADCAST) {
|
||||||
|
// Broadcast
|
||||||
setThreadMode(ThreadMode::CHANNEL, sm.channelIndex);
|
setThreadMode(ThreadMode::CHANNEL, sm.channelIndex);
|
||||||
} else if (sm.type == MessageType::DM_TO_US) {
|
} else {
|
||||||
uint32_t peer = (packet.from == 0) ? sm.dest : sm.sender;
|
// Direct message
|
||||||
|
uint32_t peer = (packet.from != 0) ? sm.sender : sm.dest;
|
||||||
setThreadMode(ThreadMode::DIRECT, -1, peer);
|
setThreadMode(ThreadMode::DIRECT, -1, peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ int getThreadChannel();
|
|||||||
// Getter for current peer (valid if mode == DIRECT)
|
// Getter for current peer (valid if mode == DIRECT)
|
||||||
uint32_t getThreadPeer();
|
uint32_t getThreadPeer();
|
||||||
|
|
||||||
// --- Registry accessors for menuHandler ---
|
// Registry accessors for menuHandler
|
||||||
const std::vector<int> &getSeenChannels();
|
const std::vector<int> &getSeenChannels();
|
||||||
const std::vector<uint32_t> &getSeenPeers();
|
const std::vector<uint32_t> &getSeenPeers();
|
||||||
|
|
||||||
|
|||||||
@@ -1062,8 +1062,9 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
|
|||||||
|
|
||||||
sm.sender = nodeDB->getNodeNum(); // us
|
sm.sender = nodeDB->getNodeNum(); // us
|
||||||
sm.channelIndex = channel;
|
sm.channelIndex = channel;
|
||||||
strncpy(sm.text, message, MAX_MESSAGE_SIZE - 1);
|
size_t len = strnlen(message, MAX_MESSAGE_SIZE - 1);
|
||||||
sm.text[MAX_MESSAGE_SIZE - 1] = '\0';
|
sm.textOffset = MessageStore::storeText(message, len);
|
||||||
|
sm.textLength = len;
|
||||||
|
|
||||||
// Classify broadcast vs DM
|
// Classify broadcast vs DM
|
||||||
if (dest == NODENUM_BROADCAST) {
|
if (dest == NODENUM_BROADCAST) {
|
||||||
|
|||||||
Reference in New Issue
Block a user