mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-08-06 20:26:12 +00:00
feat(bot): Actions commands, multi-trigger, and user GPIO pins
- Auto-Reply Bot gains Actions (!buzz/!gps/!advert) behind a new per-target
toggle nested under Commands (bot_actions_dm/ch/room); off by default.
- Bot Trigger fields accept comma-separated multiple phrases, matching any
one fires the reply.
- New user-assignable GPIO feature (Wio Tracker L1): !gpio1..!gpio4 bot
commands plus a Tools > GPIO screen. Each pin cycles Off/Input/Output;
GPIO1/GPIO2 (P0.02/P0.29, the nRF52840's AIN0/AIN5) also offer a read-only
Analog mode via direct SAADC access. GPIO3/GPIO4 (P0.09/P0.10) are the
chip's NFC1/NFC2 pins, repurposed as plain GPIO via a one-time UICR
NFCPINS bit-clear in initVariant() (adapted from Adafruit's own
nfc_to_gpio example) -- confirmed working on real hardware.
- Fix: DM/room reply-prefix ("@[nick] ") stripping happened at the wrong
layer, hiding the "To:" header on DM replies and leaking the raw prefix
into room messages' list view; a related mismatch had the history
scrollbar's sizing pass wrap room messages with the sender name still
attached, disagreeing with the actual rendered text.
Build-verified: WioTrackerL1_companion_solo_dual and
WioTrackerL1Eink_companion_solo_dual both compile and link clean
(sizeof(NodePrefs) confirmed 2720 via real build, not guessed).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -136,7 +136,10 @@ class MessagesScreen : public UIScreen {
|
||||
struct HistScroll { bool need; int reserve; long total_px, scroll_px; int view_px; };
|
||||
|
||||
// `getBody(idx)` returns the body text for list item idx (the part that wraps),
|
||||
// or nullptr to fall back to a fixed 2-line box.
|
||||
// or nullptr to fall back to a fixed 2-line box. Must return exactly what the
|
||||
// real per-item render pass wraps (sender split off, reply prefix stripped) --
|
||||
// this function doesn't reprocess it, so a mismatch here just means this sizing
|
||||
// pass and the real render disagree on line count.
|
||||
template <class GetBody>
|
||||
HistScroll computeHistScroll(DisplayDriver& display, bool portrait, int count, int scroll,
|
||||
int hist_start_y, int cby, int lh, GetBody getBody) {
|
||||
@@ -161,7 +164,7 @@ class MessagesScreen : public UIScreen {
|
||||
auto boxH = [&](int idx, int rsv) -> int {
|
||||
const char* body = getBody(idx);
|
||||
if (!body) return fixed_bh;
|
||||
display.translateUTF8ToBlocks(s_wrap_trans, skipReplyPrefix(body), sizeof(s_wrap_trans));
|
||||
display.translateUTF8ToBlocks(s_wrap_trans, body, sizeof(s_wrap_trans));
|
||||
int nl = FullscreenMsgView::wrapLines(display, s_wrap_trans, display.width() - 6 - rsv, s_wrap_lines, 8);
|
||||
return (1 + (nl > 0 ? nl : 1)) * lh + 1;
|
||||
};
|
||||
@@ -191,9 +194,20 @@ class MessagesScreen : public UIScreen {
|
||||
// stored "Sender: text" (MyMesh::queueMessage); split that off so each line is
|
||||
// attributed to its guest. Outgoing → "Me"; plain DMs (or no separator) keep
|
||||
// the contact name and the text unchanged.
|
||||
//
|
||||
// Does NOT strip a leading "@[nick] " reply prefix — that's the caller's call,
|
||||
// and it must be made exactly once: FullscreenMsgView::render() parses it
|
||||
// itself (for the "To:" header), so callers feeding it must pass this
|
||||
// function's result straight through; the compact list view has no such
|
||||
// parsing of its own, so those callers must wrap the result in
|
||||
// skipReplyPrefix(). Stripping it in here unconditionally used to double-strip
|
||||
// the DM case (hiding FullscreenMsgView's "To:" header entirely) while never
|
||||
// stripping the room case (the split happens after this used to run), which is
|
||||
// why replies' addressee went undetected inconsistently between DM/room/list/
|
||||
// fullscreen.
|
||||
const char* dmDisplayParts(const DmHistEntry& e, bool is_room, const char* contact_name,
|
||||
char* sender_buf, int sender_cap) const {
|
||||
const char* body = skipReplyPrefix(e.text);
|
||||
const char* body = e.text;
|
||||
if (e.outgoing) {
|
||||
strncpy(sender_buf, "Me", sender_cap - 1);
|
||||
} else if (is_room) {
|
||||
@@ -1031,6 +1045,8 @@ public:
|
||||
if (ring_pos >= 0) {
|
||||
const DmHistEntry& e = _history.dmAtPos(ring_pos);
|
||||
char sender_buf[33];
|
||||
// No skipReplyPrefix() here -- _dm_fs.render() parses "@[nick] " itself
|
||||
// (for the "To:" header); stripping it here first would hide it there.
|
||||
const char* body = dmDisplayParts(e, _sel_contact.type == ADV_TYPE_ROOM,
|
||||
filtered_name, sender_buf, sizeof(sender_buf));
|
||||
const char* sender = sender_buf;
|
||||
@@ -1073,7 +1089,15 @@ public:
|
||||
hist_start_y, cby, lh,
|
||||
[&](int idx) -> const char* {
|
||||
int rp = _history.dmHistEntryForContact(_sel_contact.id.pub_key, idx);
|
||||
return rp >= 0 ? _history.dmAtPos(rp).text : nullptr;
|
||||
if (rp < 0) return nullptr;
|
||||
// Must match the per-item body extraction below (dmDisplayParts +
|
||||
// skipReplyPrefix) exactly, or this sizing pass and the real render
|
||||
// pass disagree on wrapped line count for room posts -- previously
|
||||
// this returned the raw "Sender: text" unsplit, wrapping the sender
|
||||
// name in with the body and mis-sizing the box.
|
||||
char tmp_sender[33];
|
||||
return skipReplyPrefix(dmDisplayParts(_history.dmAtPos(rp), is_room, filtered_name,
|
||||
tmp_sender, sizeof(tmp_sender)));
|
||||
});
|
||||
int reserve = hs.reserve;
|
||||
{
|
||||
@@ -1091,7 +1115,7 @@ public:
|
||||
int rp = _history.dmHistEntryForContact(_sel_contact.id.pub_key, _dm_hist_scroll + ii);
|
||||
if (rp >= 0) {
|
||||
char hsb[33];
|
||||
const char* hbody = dmDisplayParts(_history.dmAtPos(rp), is_room, filtered_name, hsb, sizeof(hsb));
|
||||
const char* hbody = skipReplyPrefix(dmDisplayParts(_history.dmAtPos(rp), is_room, filtered_name, hsb, sizeof(hsb)));
|
||||
display.translateUTF8ToBlocks(s_wrap_trans, hbody, sizeof(s_wrap_trans));
|
||||
int nl = FullscreenMsgView::wrapLines(display, s_wrap_trans, display.width() - 6 - reserve, s_wrap_lines, 8);
|
||||
bh = (1 + (nl > 0 ? nl : 1)) * lh + 1;
|
||||
@@ -1115,7 +1139,7 @@ public:
|
||||
|
||||
const DmHistEntry& e = _history.dmAtPos(ring_pos);
|
||||
char sender_buf[33];
|
||||
const char* body = dmDisplayParts(e, is_room, filtered_name, sender_buf, sizeof(sender_buf));
|
||||
const char* body = skipReplyPrefix(dmDisplayParts(e, is_room, filtered_name, sender_buf, sizeof(sender_buf)));
|
||||
const char* sender = sender_buf;
|
||||
|
||||
char age[6]; geo::fmtAgeShort(age, sizeof(age), now_ts, e.timestamp);
|
||||
@@ -1233,7 +1257,7 @@ public:
|
||||
if (rp < 0) return nullptr;
|
||||
const char* t = _history.chAtPos(rp).text;
|
||||
const char* s = strstr(t, ": ");
|
||||
return s ? s + 2 : t;
|
||||
return skipReplyPrefix(s ? s + 2 : t);
|
||||
});
|
||||
int reserve = hs.reserve;
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user