mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-24 11:56:38 +00:00
feat(mesh): mirror app-originated DM/channel sends into on-device history
CMD_SEND_TXT_MSG and CMD_SEND_CHANNEL_TXT_MSG (the phone app's send path) transmitted over the mesh but never touched the device's own MessagesScreen history, unlike a message composed on-device (MessagesScreen::afterSend) -- so a DM/channel post sent from the app was invisible if that same conversation was later opened on the device's own screen. Both handlers now also call into the same history-store entry points incoming messages use. Also wires up delivery-status parity with an on-device send, not just the raw text: - Channels: arms the existing "relayed into mesh" repeater-echo tracker (trackRelaySend()/armChannelRelay()) on the new entry -- sendGroupMessage already runs that tracker regardless of who originated the send, this just attaches it to the right history entry. Required threading a ring position back out through AbstractUITask::addChannelMsg (now returns int) and a new armChannelRelay() passthrough. - DMs: addDMMsg gained ack_tag/ack_deadline_ms/resends params (threaded through MessageHistory -> MessagesScreen -> AbstractUITask/UITask) so an app-sent DM gets the same pending -> \xe2\x9c\x93/\xe2\x9c\x97 status the on-device compose path shows. resends stays 0 deliberately: the app owns its own retry decision, so this only drives the on-screen status, never a second, independent auto-resend from the device itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,8 +84,19 @@ public:
|
|||||||
virtual void msgRead(int msgcount) = 0;
|
virtual void msgRead(int msgcount) = 0;
|
||||||
virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount, uint8_t contact_type = 0, const uint8_t* pub_key = nullptr) = 0;
|
virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount, uint8_t contact_type = 0, const uint8_t* pub_key = nullptr) = 0;
|
||||||
virtual void notify(UIEventType t = UIEventType::none) = 0;
|
virtual void notify(UIEventType t = UIEventType::none) = 0;
|
||||||
virtual void addChannelMsg(uint8_t channel_idx, const char* text, uint32_t timestamp = 0) {}
|
// Returns the new entry's ring position (see MessageHistory::addChannelMsg),
|
||||||
virtual void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp = 0) {}
|
// or -1 on a UI variant that doesn't track history (default no-op below) --
|
||||||
|
// callers that need it (to then arm a relay-echo tracker) should check for
|
||||||
|
// that instead of assuming a valid position.
|
||||||
|
virtual int addChannelMsg(uint8_t channel_idx, const char* text, uint32_t timestamp = 0) { return -1; }
|
||||||
|
// Arms the "relayed into mesh" tracker (a heard repeater rebroadcast) on the
|
||||||
|
// entry at ring position pos, e.g. right after addChannelMsg for a channel
|
||||||
|
// send this device just originated. seq: MyMesh::lastChannelRelaySeq().
|
||||||
|
virtual void armChannelRelay(int pos, uint32_t seq) {}
|
||||||
|
// ack_tag/ack_deadline_ms/resends: pending-ACK tracking for an outgoing DM
|
||||||
|
// (0 = none, e.g. incoming or "no ack expected") -- see MessageHistory::addDMMsg.
|
||||||
|
virtual void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp = 0,
|
||||||
|
uint32_t ack_tag = 0, uint32_t ack_deadline_ms = 0, uint8_t resends = 0) {}
|
||||||
// A node shared its current position via a [LOC] message. pub_key is the
|
// A node shared its current position via a [LOC] message. pub_key is the
|
||||||
// sender's key prefix for a verified DM share, or null for a channel share
|
// sender's key prefix for a verified DM share, or null for a channel share
|
||||||
// (keyed by name, best-effort). Default no-op so UI variants opt in.
|
// (keyed by name, best-effort). Default no-op so UI variants opt in.
|
||||||
|
|||||||
@@ -1972,6 +1972,22 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||||||
memcpy(&out_frame[2], &expected_ack, 4);
|
memcpy(&out_frame[2], &expected_ack, 4);
|
||||||
memcpy(&out_frame[6], &est_timeout, 4);
|
memcpy(&out_frame[6], &est_timeout, 4);
|
||||||
_serial->writeFrame(out_frame, 10);
|
_serial->writeFrame(out_frame, 10);
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
// Mirror this app-originated DM into the on-device history too, same as
|
||||||
|
// a message composed on-device (MessagesScreen::afterSend) -- otherwise
|
||||||
|
// the two queues drift and a DM sent from the phone app never shows up
|
||||||
|
// if that same contact is later opened on the device's own screen.
|
||||||
|
// ack_tag/ack_deadline mirror the on-device pending->()/x delivery marker
|
||||||
|
// too (same "+4s margin over the estimate" the on-device compose path
|
||||||
|
// uses); resends stays 0 -- the app already owns its own resend/retry
|
||||||
|
// decision, so this only drives the on-screen status, never a second,
|
||||||
|
// independent auto-resend from the device itself.
|
||||||
|
if (_ui && txt_type == TXT_TYPE_PLAIN) {
|
||||||
|
uint32_t ack_deadline_ms = expected_ack ? (millis() + est_timeout + 4000) : 0;
|
||||||
|
_ui->addDMMsg(recipient->id.pub_key, true, text, msg_timestamp, expected_ack, ack_deadline_ms, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
writeErrFrame(recipient == NULL
|
writeErrFrame(recipient == NULL
|
||||||
@@ -1994,6 +2010,28 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||||||
bool success = getChannel(channel_idx, channel);
|
bool success = getChannel(channel_idx, channel);
|
||||||
if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) {
|
if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) {
|
||||||
writeOKFrame();
|
writeOKFrame();
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
// Mirror this app-originated channel post into the on-device history,
|
||||||
|
// same "Me: " framing MessagesScreen::afterSend uses for an on-device
|
||||||
|
// compose -- otherwise the two queues drift and a post sent from the
|
||||||
|
// phone app never shows up if that channel is later opened on-device.
|
||||||
|
// text isn't guaranteed null-terminated (len - i is its real length,
|
||||||
|
// same bound sendGroupMessage above was just given), so bound the copy.
|
||||||
|
if (_ui) {
|
||||||
|
char entry[MAX_TEXT_LEN + 8]; // "Me: " + text
|
||||||
|
int tlen = len - i;
|
||||||
|
if (tlen > MAX_TEXT_LEN) tlen = MAX_TEXT_LEN;
|
||||||
|
snprintf(entry, sizeof(entry), "Me: %.*s", tlen, text);
|
||||||
|
int pos = _ui->addChannelMsg(channel_idx, entry, msg_timestamp);
|
||||||
|
// Same "relayed into mesh" marker an on-device channel send arms (see
|
||||||
|
// MessagesScreen::afterSend): sendGroupMessage above already went
|
||||||
|
// through sendFloodScoped(GroupChannel&, ...), which calls
|
||||||
|
// trackRelaySend() unconditionally, so lastChannelRelaySeq() is
|
||||||
|
// already the seq for the send that was just made -- this just
|
||||||
|
// attaches it to the matching history entry.
|
||||||
|
if (pos >= 0) _ui->armChannelRelay(pos, lastChannelRelaySeq());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
|
writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,8 +212,13 @@ public:
|
|||||||
_dm_hist[pos].resends_left = (outgoing && ack_tag) ? resends : 0;
|
_dm_hist[pos].resends_left = (outgoing && ack_tag) ? resends : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ack_tag/ack_deadline_ms/resends let an outgoing DM (e.g. one the phone app
|
||||||
|
// just sent via CMD_SEND_TXT_MSG) carry the same pending-ACK tracking a
|
||||||
|
// message composed on-device gets from storeDMMsg() directly — otherwise it
|
||||||
|
// shows with no delivery status at all. Unused (0) for incoming.
|
||||||
void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text,
|
void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text,
|
||||||
uint32_t sender_timestamp = 0) {
|
uint32_t sender_timestamp = 0, uint32_t ack_tag = 0,
|
||||||
|
uint32_t ack_deadline_ms = 0, uint8_t resends = 0) {
|
||||||
// Drop retried copies of an incoming DM: a resend reuses the sender's
|
// Drop retried copies of an incoming DM: a resend reuses the sender's
|
||||||
// timestamp and text but carries a fresh packet hash, so the mesh dup-filter
|
// timestamp and text but carries a fresh packet hash, so the mesh dup-filter
|
||||||
// lets it through. Match on prefix + sender_timestamp + text to suppress it.
|
// lets it through. Match on prefix + sender_timestamp + text to suppress it.
|
||||||
@@ -225,7 +230,7 @@ public:
|
|||||||
return; // duplicate retry — already in history
|
return; // duplicate retry — already in history
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
storeDMMsg(pub_key, outgoing, text, 0, 0, outgoing ? 0 : sender_timestamp, 0);
|
storeDMMsg(pub_key, outgoing, text, ack_tag, ack_deadline_ms, sender_timestamp, resends);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dmHistCountForContact(const uint8_t* prefix) const {
|
int dmHistCountForContact(const uint8_t* prefix) const {
|
||||||
|
|||||||
@@ -661,10 +661,12 @@ public:
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
void markChannelRelayed(uint32_t seq) { _history.markChannelRelayed(seq); }
|
void markChannelRelayed(uint32_t seq) { _history.markChannelRelayed(seq); }
|
||||||
|
void armChannelRelay(int pos, uint32_t seq) { _history.armChannelRelay(pos, seq); }
|
||||||
void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text,
|
void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text,
|
||||||
uint32_t sender_timestamp = 0) {
|
uint32_t sender_timestamp = 0, uint32_t ack_tag = 0,
|
||||||
|
uint32_t ack_deadline_ms = 0, uint8_t resends = 0) {
|
||||||
bool viewing = (_phase == DM_HIST && memcmp(_sel_contact.id.pub_key, pub_key, 4) == 0);
|
bool viewing = (_phase == DM_HIST && memcmp(_sel_contact.id.pub_key, pub_key, 4) == 0);
|
||||||
_history.addDMMsg(pub_key, outgoing, text, sender_timestamp);
|
_history.addDMMsg(pub_key, outgoing, text, sender_timestamp, ack_tag, ack_deadline_ms, resends);
|
||||||
if (viewing && _dm_hist_sel > 0) { _dm_hist_sel++; _dm_hist_scroll++; } // see addChannelMsg
|
if (viewing && _dm_hist_sel > 0) { _dm_hist_sel++; _dm_hist_scroll++; } // see addChannelMsg
|
||||||
}
|
}
|
||||||
void markDmDelivered(uint32_t ack_crc) { _history.markDmDelivered(ack_crc); }
|
void markDmDelivered(uint32_t ack_crc) { _history.markDmDelivered(ack_crc); }
|
||||||
|
|||||||
@@ -1766,9 +1766,13 @@ int UITask::getRecentDMContacts(uint8_t out[][NodePrefs::FAVOURITE_PREFIX_LEN],
|
|||||||
return ((MessagesScreen*)messages_screen)->getRecentDMContacts(out, max);
|
return ((MessagesScreen*)messages_screen)->getRecentDMContacts(out, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::addChannelMsg(uint8_t channel_idx, const char* text, uint32_t timestamp) {
|
int UITask::addChannelMsg(uint8_t channel_idx, const char* text, uint32_t timestamp) {
|
||||||
_last_notif_ch_idx = (int)channel_idx;
|
_last_notif_ch_idx = (int)channel_idx;
|
||||||
((MessagesScreen*)messages_screen)->addChannelMsg(channel_idx, text, timestamp);
|
return ((MessagesScreen*)messages_screen)->addChannelMsg(channel_idx, text, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::armChannelRelay(int pos, uint32_t seq) {
|
||||||
|
((MessagesScreen*)messages_screen)->armChannelRelay(pos, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
int UITask::getChannelUnreadCount() const {
|
int UITask::getChannelUnreadCount() const {
|
||||||
@@ -1801,8 +1805,9 @@ void UITask::onAdminReply(const uint8_t* pub_key, const char* text) {
|
|||||||
_next_refresh = 0; // same reasoning as onRoomLoginResult above
|
_next_refresh = 0; // same reasoning as onRoomLoginResult above
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp) {
|
void UITask::addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp,
|
||||||
((MessagesScreen*)messages_screen)->addDMMsg(pub_key, outgoing, text, sender_timestamp);
|
uint32_t ack_tag, uint32_t ack_deadline_ms, uint8_t resends) {
|
||||||
|
((MessagesScreen*)messages_screen)->addDMMsg(pub_key, outgoing, text, sender_timestamp, ack_tag, ack_deadline_ms, resends);
|
||||||
}
|
}
|
||||||
|
|
||||||
int UITask::getDMUnreadTotal() const {
|
int UITask::getDMUnreadTotal() const {
|
||||||
|
|||||||
@@ -385,8 +385,10 @@ public:
|
|||||||
void stopMelody();
|
void stopMelody();
|
||||||
bool isMelodyPlaying();
|
bool isMelodyPlaying();
|
||||||
void showAlert(const char* text, int duration_millis);
|
void showAlert(const char* text, int duration_millis);
|
||||||
void addChannelMsg(uint8_t channel_idx, const char* text, uint32_t timestamp = 0) override;
|
int addChannelMsg(uint8_t channel_idx, const char* text, uint32_t timestamp = 0) override;
|
||||||
void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp = 0) override;
|
void armChannelRelay(int pos, uint32_t seq) override;
|
||||||
|
void addDMMsg(const uint8_t* pub_key, bool outgoing, const char* text, uint32_t sender_timestamp = 0,
|
||||||
|
uint32_t ack_tag = 0, uint32_t ack_deadline_ms = 0, uint8_t resends = 0) override;
|
||||||
void onMsgAck(uint32_t ack_crc) override;
|
void onMsgAck(uint32_t ack_crc) override;
|
||||||
void onChannelRelayed(uint32_t seq) override;
|
void onChannelRelayed(uint32_t seq) override;
|
||||||
void onRoomLoginResult(const uint8_t* pub_key, bool success, uint8_t permissions) override;
|
void onRoomLoginResult(const uint8_t* pub_key, bool success, uint8_t permissions) override;
|
||||||
|
|||||||
Reference in New Issue
Block a user