mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 14:58:12 +00:00
fix(ui): Admin login could hang forever on "Logging in..."
AdminScreen's LOGIN phase had no timeout, unlike its COMMAND phase (_cmd_deadline_ms). If a login reply never arrived -- most commonly a saved password gone stale after the remote node's password changed, silently dropped instead of nacked -- the screen stayed stuck with only a manual Cancel to escape. sendRoomLogin() now returns the same est_timeout sendAdminCommand() already exposes; AdminScreen uses it to arm a deadline (poll(), mirroring the COMMAND-phase pattern) that forgets the stale password and returns to the picker on expiry, same as an explicit login rejection already does. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -222,8 +222,7 @@ public:
|
|||||||
// command source, so this reuses the same sendLogin() the BLE CMD_SEND_LOGIN
|
// command source, so this reuses the same sendLogin() the BLE CMD_SEND_LOGIN
|
||||||
// path uses; the async result lands in onContactResponse() and is pushed to
|
// path uses; the async result lands in onContactResponse() and is pushed to
|
||||||
// the UI via AbstractUITask::onRoomLoginResult().
|
// the UI via AbstractUITask::onRoomLoginResult().
|
||||||
bool sendRoomLogin(const ContactInfo& contact, const char* password) {
|
bool sendRoomLogin(const ContactInfo& contact, const char* password, uint32_t& est_timeout) {
|
||||||
uint32_t est_timeout;
|
|
||||||
if (sendLogin(contact, password, est_timeout) == MSG_SEND_FAILED) return false;
|
if (sendLogin(contact, password, est_timeout) == MSG_SEND_FAILED) return false;
|
||||||
clearPendingReqs();
|
clearPendingReqs();
|
||||||
memcpy(&ui_pending_login, contact.id.pub_key, 4); // match this in onContactResponse()
|
memcpy(&ui_pending_login, contact.id.pub_key, 4); // match this in onContactResponse()
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ class AdminScreen : public UIScreen {
|
|||||||
// was submitted. Distinct from the keyboard being open (kb().render()/
|
// was submitted. Distinct from the keyboard being open (kb().render()/
|
||||||
// handleInput() only run while this is false).
|
// handleInput() only run while this is false).
|
||||||
bool _login_waiting = false;
|
bool _login_waiting = false;
|
||||||
|
// Deadline for _login_waiting, mirroring _cmd_deadline_ms/_waiting in the
|
||||||
|
// COMMAND phase below -- without it, a login reply that never arrives (e.g.
|
||||||
|
// the remote's password changed since it was saved, and it silently drops
|
||||||
|
// instead of nacking) leaves the screen stuck on "Logging in..." forever
|
||||||
|
// with only a manual Cancel to escape. See poll().
|
||||||
|
uint32_t _login_deadline_ms = 0;
|
||||||
|
|
||||||
// One-slot memo: the last contact successfully admin-logged-into this visit,
|
// One-slot memo: the last contact successfully admin-logged-into this visit,
|
||||||
// so re-entering COMMAND for the same target right after doesn't require
|
// so re-entering COMMAND for the same target right after doesn't require
|
||||||
@@ -151,9 +157,10 @@ class AdminScreen : public UIScreen {
|
|||||||
void startLoginWithSaved(const char* password) {
|
void startLoginWithSaved(const char* password) {
|
||||||
strncpy(_login_pw, password, sizeof(_login_pw) - 1);
|
strncpy(_login_pw, password, sizeof(_login_pw) - 1);
|
||||||
_login_pw[sizeof(_login_pw) - 1] = '\0';
|
_login_pw[sizeof(_login_pw) - 1] = '\0';
|
||||||
bool sent = the_mesh.sendRoomLogin(_target, _login_pw);
|
uint32_t est_timeout = 0;
|
||||||
|
bool sent = the_mesh.sendRoomLogin(_target, _login_pw, est_timeout);
|
||||||
_task->showAlert(sent ? "Logging in..." : "Login failed", sent ? 1000 : 1500);
|
_task->showAlert(sent ? "Logging in..." : "Login failed", sent ? 1000 : 1500);
|
||||||
if (sent) { _login_waiting = true; _phase = LOGIN; }
|
if (sent) { _login_waiting = true; _login_deadline_ms = millis() + est_timeout + 4000; _phase = LOGIN; }
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendCommand() {
|
void sendCommand() {
|
||||||
@@ -410,6 +417,18 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void poll() override {
|
void poll() override {
|
||||||
|
if (_phase == LOGIN && _login_waiting && (int32_t)(millis() - _login_deadline_ms) >= 0) {
|
||||||
|
_login_waiting = false;
|
||||||
|
// No response at all is ambiguous (could be a wrong/stale password, could
|
||||||
|
// just be out of range) -- but a saved password that's gone stale (the
|
||||||
|
// remote's password changed) is exactly this: silence, not a nack. Treat
|
||||||
|
// it the same as onRoomLoginResult()'s explicit-failure branch: forget it
|
||||||
|
// so the next attempt prompts fresh instead of retrying the same dead
|
||||||
|
// password forever.
|
||||||
|
the_mesh.forgetRoomPassword(_target.id.pub_key);
|
||||||
|
_task->showAlert("Login failed (timeout)", 1400);
|
||||||
|
returnToOrigin();
|
||||||
|
}
|
||||||
if (_phase == COMMAND && _waiting && (int32_t)(millis() - _cmd_deadline_ms) >= 0) {
|
if (_phase == COMMAND && _waiting && (int32_t)(millis() - _cmd_deadline_ms) >= 0) {
|
||||||
_waiting = false;
|
_waiting = false;
|
||||||
if (_fetch_for_edit) { fallBackToBlankEdit(); _task->showAlert("Fetch failed - enter value", 1400); }
|
if (_fetch_for_edit) { fallBackToBlankEdit(); _task->showAlert("Fetch failed - enter value", 1400); }
|
||||||
@@ -488,9 +507,10 @@ public:
|
|||||||
} else if (r == KeyboardWidget::DONE) {
|
} else if (r == KeyboardWidget::DONE) {
|
||||||
strncpy(_login_pw, kb().buf, sizeof(_login_pw) - 1);
|
strncpy(_login_pw, kb().buf, sizeof(_login_pw) - 1);
|
||||||
_login_pw[sizeof(_login_pw) - 1] = '\0';
|
_login_pw[sizeof(_login_pw) - 1] = '\0';
|
||||||
bool sent = the_mesh.sendRoomLogin(_target, _login_pw);
|
uint32_t est_timeout = 0;
|
||||||
|
bool sent = the_mesh.sendRoomLogin(_target, _login_pw, est_timeout);
|
||||||
_task->showAlert(sent ? "Logging in..." : "Login failed", sent ? 1000 : 1500);
|
_task->showAlert(sent ? "Logging in..." : "Login failed", sent ? 1000 : 1500);
|
||||||
if (sent) _login_waiting = true; else returnToOrigin();
|
if (sent) { _login_waiting = true; _login_deadline_ms = millis() + est_timeout + 4000; } else returnToOrigin();
|
||||||
// else: stay in LOGIN until onRoomLoginResult() fires above.
|
// else: stay in LOGIN until onRoomLoginResult() fires above.
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -708,7 +708,9 @@ public:
|
|||||||
void startRoomLogin(const char* password) {
|
void startRoomLogin(const char* password) {
|
||||||
strncpy(_login_pw, password, sizeof(_login_pw) - 1);
|
strncpy(_login_pw, password, sizeof(_login_pw) - 1);
|
||||||
_login_pw[sizeof(_login_pw) - 1] = 0;
|
_login_pw[sizeof(_login_pw) - 1] = 0;
|
||||||
bool sent = the_mesh.sendRoomLogin(_sel_contact, password);
|
uint32_t est_timeout = 0; // this screen's login isn't a blocking wait (see
|
||||||
|
// onRoomLoginResult() below), so no deadline needed
|
||||||
|
bool sent = the_mesh.sendRoomLogin(_sel_contact, password, est_timeout);
|
||||||
_task->showAlert(sent ? "Logging in..." : "Login failed", sent ? 1000 : 1500);
|
_task->showAlert(sent ? "Logging in..." : "Login failed", sent ? 1000 : 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- **Tools › Admin login could get stuck on "Logging in..." forever** if the remote node never sent back a reply — most commonly after its admin/room password was changed, since the on-device UI auto-retries the old saved password with no timeout to fall back on. It now gives up after the same estimated-timeout window the rest of Admin's commands use, forgets the stale saved password, and returns you to the picker to try again with the new one.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## MeshCore Solo Companion Firmware v1.23
|
## MeshCore Solo Companion Firmware v1.23
|
||||||
|
|
||||||
### What's new
|
### What's new
|
||||||
|
|||||||
Reference in New Issue
Block a user