fix(android): a network call that never returns no longer kills the interface

Reported symptom, with the local server stopped: Sync leaves the app inert -
neither Open vault nor Sync responds, and nothing is displayed. Log and
Server settings still work, because they are local to the page.

The log proves where it stops: the GET line is there, the "HTTP ... in ... ms"
line that immediately follows the call is not. So the request never returned,
SetBusy(False) never ran, and the two actions stayed disabled - a disabled
button fires no click, hence the silence. THTTPClient is given 15 s timeouts
and the Android request does inherit them, so this should not happen; rather
than keep chasing why it did, the situation is now impossible.

A deadline timer, armed while a request is in flight and longer than the HTTP
timeouts so the precise network error still wins when it works. On expiry the
interface comes back and says so. The thread is abandoned, not cancelled: it
dies when its socket fails, and if it somehow succeeds its result still shows.

The page's bridge queue had the same shape of bug: a throw while removing the
iframe left "sending" true forever, and every later command was dropped in
silence. The removal is now in a try/finally.

Also fixed while in there: SetBusy hardcoded "Sync" as the button label, so a
vault-less device lost "Download vault" on every busy transition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-17 13:11:21 +01:00
parent 0bcf9b95a2
commit c215a5755d
2 changed files with 55 additions and 5 deletions
+12 -3
View File
@@ -285,8 +285,15 @@ function drain() {
f.src = 'cmd://' + queue.shift();
document.body.appendChild(f);
setTimeout(function () {
f.parentNode.removeChild(f);
drain();
// LE try/finally EST L'INTERET DE CE BLOC. Sans lui, une exception au
// retrait de l'iframe laisse "sending" a vrai pour toujours : plus une
// seule commande ne repart, et l'interface devient muette sans rien
// signaler. Le meme piege existait cote Delphi sur l'indicateur d'occupation.
try {
if (f.parentNode) f.parentNode.removeChild(f);
} finally {
drain();
}
}, 60);
}
@@ -496,7 +503,9 @@ var PM = {
setActions: function (canOpen, syncLabel, busy) {
var o = document.getElementById('btnOpen'), s = document.getElementById('btnSync');
o.hidden = !canOpen;
s.innerHTML = '&#8635;&nbsp; ' + syncLabel;
// textContent et non innerHTML : l'intitule vient de Delphi, et une valeur
// interpretee comme du balisage n'a pas de raison de l'etre.
s.textContent = '\u21BB ' + syncLabel;
o.disabled = busy; s.disabled = busy;
o.style.opacity = busy ? .4 : 1;
s.style.opacity = busy ? .4 : 1;