touch: fix manual telemetry request reliability + async spinner blink

Two bugs in the manual telemetry request path (the Request button in the
telemetry window):

1. Usually only the second request succeeded. The guest LOGIN and the
   telemetry REQ were sent back-to-back, but a repeater drops a REQ from a
   sender it hasn't added to its ACL yet, and the ACL entry isn't committed
   by the time the first REQ is processed — so the first request got no reply
   and the user had to tap twice. Defer the REQ: uiSendRequestAfterGuestLogin()
   sends only the guest LOGIN and fires the REQ from onContactResponse once the
   LOGIN-OK arrives, by which point we're in the repeater's ACL and a direct
   out_path has been learned, so it lands on the first try. Also adds a
   single-flight guard on telemetryRequestNow(), holds auto-poll while a manual
   request is pending (so it can't clobber the pending reply tag), and disarms
   the deferred login on timeout so a late LOGIN-OK can't fire a stale REQ.

2. The status-bar async spinner only blinked for ~1.5s on a telemetry request
   (the markMeshRequest window) then froze, instead of blinking for the whole
   pending window like a status ping. Its active predicate watched the ping
   deadline but not the telemetry timers; OR in the telemetry pending state.

Auto-poll's best-effort chained send and the admin-console login path are
left unchanged.
This commit is contained in:
Dan Vybiral
2026-07-01 20:52:45 +02:00
committed by Kaj
parent 3a1495784e
commit 96dffaef58
3 changed files with 75 additions and 2 deletions
+20
View File
@@ -2200,6 +2200,26 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
}
return;
}
// Deferred guest-login-then-request (uiSendRequestAfterGuestLogin): the touch
// UI sent only a blank-password LOGIN and is waiting on the LOGIN-OK before
// issuing the STATUS/TELEMETRY REQ. While armed we have sent no REQ to this
// contact, so any RESPONSE from it here is the login reply. On OK, fire the
// deferred REQ now — we're in the repeater's ACL and a direct path has been
// learned, so it lands first try. On a non-OK (fail) reply, just disarm and
// let the UI's reply deadline flip the window to "failed".
if (_ui_login_then && len > 4 && memcmp(&_ui_login_then, contact.id.pub_key, 4) == 0) {
const bool login_ok = (data[4] == RESP_SERVER_LOGIN_OK)
|| (len > 5 && memcmp(&data[4], "OK", 2) == 0);
const UiReqKind k = _ui_login_then_kind;
cancelUIDeferredLogin();
if (login_ok) {
ContactInfo& rc = const_cast<ContactInfo&>(contact);
if (k == UiReqKind::Telemetry) sendTelemetryRequestForUI(rc);
else sendStatusPingForUI(rc);
}
return; // login frame consumed (OK fired the REQ; fail disarmed)
}
#endif
if (pending_login && memcmp(&pending_login, contact.id.pub_key, 4) == 0) { // check for login response