Previously a single ASCII glyph collapsed SENT and DELIVERED. Wire
PacketReceipt delivery callbacks through to sideband-style single
and double checks.
LXMFManager tracks outstanding receipts (opportunistic + link
single-packet) by hash. On delivery: persist DELIVERED and fire
_statusCb. On 60s timeout: drop the entry, leave at SENT — lack
of proof over LoRa isn't proof of failure. Resource transfers stay
at SENT; microReticulum's Transport skips receipts for resource
packets and Link::start_resource_transfer exposes no concluded
callback to hook.
LvMessageView renders the glyph via applyStatusGlyph (Montserrat 12):
REFRESH for in-flight, single OK for SENT, double OK for DELIVERED,
WARNING for FAILED. Status callback matches by timestamp (~1s
tolerance) so DELIVERED lands on the right bubble after later
messages have already moved past QUEUED.
Trackball deltas crossing the nav threshold previously fired
up/down/left/right key events AND set _strongActivity=true, which
woke the screen from SCREEN_OFF on any pocket/backpack jostle and
also accumulated phantom navigation in the UI behind the dark screen.
Gate the nav-event block on isScreenOn(), mirroring the touch
suppression. Click/long-press detection stays unconditional so an
intentional trackball press still wakes the device.
The handler had no aspect filter, so it captured every announce from
every aspect of a peer (lxmf.delivery, lxmf.propagation, NomadNet
node, etc.). Each aspect has a different destination hash, so the
same Sideband/Columba peer appeared multiple times in the nodes list
— once with the display name (lxmf.delivery's app_data carries it)
and once or more as raw hex (other aspects had no/different name).
Restrict to lxmf.delivery: that's the only destination we ever route
LXMF messages to. Propagation/NomadNet visibility can come back later
behind an opt-in toggle if needed.
Note: existing saved contacts captured under other aspects will still
appear after this change since they're loaded from disk. They can be
removed manually via the contacts UI.
PATH_CONTACTS / PATH_MESSAGES / SD_PATH_CONTACTS / SD_PATH_MESSAGES
all ended in '/'. The FATFS layer used by the SD library and LittleFS
won't reliably enumerate when given a path ending in slash — opendir
returns a valid handle but readdir yields nothing. Direct file reads
(SD.open with FILE_READ for a known path) still work, which is why
settings/identity persisted but contacts and conversations vanished
on reboot.
Drop the trailing slash from the constants and add explicit "/" at
all concat sites: saveContact, removeContact, conversationDir,
sdConversationDir, and the migrateTruncatedDirs path builders.
Two pocket-carry quality-of-life additions:
1. Long-press (1.2s) trackball click blanks the screen if no LVGL
screen consumes the long-press. InputManager captures the power
state at click DOWN (_clickFromScreenOn) and only emits the
long-press if the screen was already on, so a long-press from
SCREEN_OFF wakes without immediately re-blanking.
2. Touch events are ignored entirely while screen is off, preventing
accidental wakes from pressure on the panel in a pocket/bag.
Trackball click and keyboard keys still wake normally.
InputManager now holds a Power* injected via setPowerMgr() in setup.
Sideband/Columba and other LXMF clients commonly include "/" in display
names (e.g. handle/path conventions). The allowlist in sanitizeName
stripped it, mangling "alice/columba" into "alicecolumba". Saved
contacts get re-sanitized on each load (line 393), so existing names
also lose the slash on next boot.
New public method that transitions to SCREEN_OFF on demand. A
_justWokeFromOff flag is set in activity() and cleared at the end
of loop() so a single keypress that woke the screen can't immediately
re-blank it via a hotkey/long-press handler running in the same tick.
Pure infrastructure — no UI binding included; consumers can wire it
to whatever input event they want (long-press, hotkey, etc.).
Python LXMF was sending bz2-compressed Resource transfers that
microReticulum cannot decode on ESP32 (bz2's working memory exceeds
available SRAM). The companion microReticulum commit (290a133)
removes bz2 entirely; this release advertises no-compression-support
in our LXMF announces so Python LXMF disables auto_compress when
sending to Ratdeck destinations.
Also pins lvgl and LovyanGFX minor versions in lib_deps to prevent
future fresh-clone builds from pulling incompatible 1.2.x / 8.4.x.
Thanks to @RFNexus for surfacing the ESP32 bz2 constraint.
encodeAnnounceName now emits msgpack fixarray(3):
[display_name(bin), stamp_cost=0, supported_functionality=[]]
Empty supported_functionality list = SF_COMPRESSION (=0x00) absent,
so Python LXMF disables auto_compress for our destinations and stops
shipping bz2-compressed Resources we can't decode. Always emit the
3-element form even when the name is empty — Python defaults to
auto_compress=True for legacy <3-element app_data.
Pairs with microReticulum 290a133 which removed bz2 entirely.
LovyanGFX 1.2.x bundles its own lvgl-compat headers that collide
with lvgl >=8.4 on shared types (lv_area_t, lv_font_t, etc.).
Tilde restricts fresh clones to the 1.1.x and 8.3.x ranges this
build was originally cached against, so `pio run` from a fresh
checkout doesn't randomly break.
CORE_DEBUG_LEVEL=0 drops Arduino core noise on expected-missing files, LittleFSFileSystem::open_file now actually works so path_table persists across reboots.
Link proof signature, TCP proof routing, and split-packet handling
were causing intermittent message failures with Python LXMF clients.
Route >254B messages through link delivery, add proof retry for LoRa.
Regenerated Montserrat 10/12/14 with Cyrillic and Latin Extended
Unicode ranges so messages in Russian, Ukrainian, Bulgarian, and
accented European languages render properly instead of rectangles.
Scroll: scrollToSelected() was defined but never called — LVGL didn't
know which pool row to show after viewport updates. Nodes at the
bottom of long lists were invisible. Now called after every sync.
Modal: Replace lv_obj_center() with explicit pixel positioning so
the action modal renders centered regardless of list scroll state.
Trackball: Click now sets character='\n' matching keyboard Enter for
complete event parity.