fix(android): the keyboard closes, and the write test is gone

Same origin as the sideways slide. The keyboard's validate key asks for
the next field, and the next one in the document sits on a view parked
outside the frame. Focus landed there unseen: the keyboard changed shape
- numeric for the code, alphabetic for the search - and would not close,
because the field holding it open was off screen.

No field of a resting view keeps the focus now, and the validate key
closes the keyboard instead of jumping. In the editor it saves, which is
what the key is for after typing a password.

The write test goes with it: it proved the write path before the first
real PUT, that path now carries the vault itself, and the button, its
handler, the probe and the debug hook that revealed it were only ever
scaffolding.
This commit is contained in:
r-zakarya
2026-08-21 12:39:22 +01:00
parent 7a9fe4ae77
commit 90ea09a718
2 changed files with 26 additions and 86 deletions
+26 -21
View File
@@ -224,10 +224,6 @@
<button class="btn" id="btnCfg">&#9881;&nbsp; Server settings</button> <button class="btn" id="btnCfg">&#9881;&nbsp; Server settings</button>
<button class="btn" id="btnLog">&#8801;&nbsp; Log</button> <button class="btn" id="btnLog">&#8801;&nbsp; Log</button>
</div> </div>
<!-- Essai du chemin d'ecriture, visible uniquement en build de debogage :
Delphi appelle PM.setDebug(true). Il ecrit dans un AUTRE fichier du
depot, jamais dans le coffre. -->
<button class="btn wide" id="btnWriteTest" hidden>Write test (debug)</button>
</div> </div>
<!-- UNE CROIX PAR CHAMP. Vider par la touche retour demande une quarantaine <!-- UNE CROIX PAR CHAMP. Vider par la touche retour demande une quarantaine
d'appuis sur un jeton, et laisse facilement un caractere - ce qui donne d'appuis sur un jeton, et laisse facilement un caractere - ce qui donne
@@ -399,14 +395,6 @@ document.getElementById('btnLog').addEventListener('click', function () {
var l = document.getElementById('log'); var l = document.getElementById('log');
l.hidden = !l.hidden; l.hidden = !l.hidden;
}); });
document.getElementById('btnWriteTest').addEventListener('click', function () {
// pushConfig D'ABORD, comme Sync. Sans cela, un jeton qui vient d'etre colle
// et dont le champ n'a pas encore perdu le focus n'est pas parti vers Delphi,
// qui agit alors avec l'ancien - et rend un 403 parfaitement trompeur.
pushConfig();
document.getElementById('log').hidden = false;
send('writetest');
});
function esc(s) { function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) {
@@ -593,12 +581,6 @@ function openEditor(k) {
var input = document.getElementById('editval'); var input = document.getElementById('editval');
input.value = detailRows[k].v; input.value = detailRows[k].v;
input.focus(); input.focus();
input.addEventListener('keydown', function (ev) {
if (ev.key !== 'Enter') return;
ev.preventDefault();
var save = document.querySelector('[data-editsave]');
if (save && !save.disabled) save.click();
});
send('touch'); send('touch');
} }
@@ -618,6 +600,32 @@ addEventListener('scroll', function (ev) {
if (box && box.scrollLeft && box.id !== 'folders') box.scrollLeft = 0; if (box && box.scrollLeft && box.id !== 'folders') box.scrollLeft = 0;
}, true); }, true);
// AUCUN CHAMP D'UNE VUE AU REPOS NE GARDE LE FOCUS.
//
// Meme origine que le glissement ci-dessus. La touche de validation du clavier
// demande "champ suivant", et le suivant dans le document est sur une vue
// rangee hors du cadre. On y atterrissait sans rien voir : le clavier changeait
// de forme - numerique pour le code, alphabetique pour la recherche - et ne se
// refermait plus, puisque le champ qui le tenait ouvert etait hors de l'ecran.
addEventListener('focusin', function (ev) {
var scr = ev.target.closest && ev.target.closest('.screen');
if (scr && scr.id !== view) ev.target.blur();
});
// La validation ferme le clavier au lieu de sauter au champ suivant. Dans
// l'editeur, elle enregistre : c'est ce qu'on attend de cette touche apres
// avoir tape un mot de passe.
addEventListener('keydown', function (ev) {
var el = ev.target;
if (ev.key !== 'Enter' || !el.classList || !el.classList.contains('field-in'))
return;
ev.preventDefault();
el.blur();
if (el.id !== 'editval') return;
var save = document.querySelector('[data-editsave]');
if (save && !save.disabled) save.click();
});
function goList() { function goList() {
document.body.classList.remove('on-detail'); document.body.classList.remove('on-detail');
document.body.classList.add('on-list'); document.body.classList.add('on-list');
@@ -644,9 +652,6 @@ var PM = {
renderRows(); renderRows();
}, },
setDebug: function (on) {
document.getElementById('btnWriteTest').hidden = !on;
},
/// Sort du mode edition sans rien demander a Delphi. Sert aux refus /// Sort du mode edition sans rien demander a Delphi. Sert aux refus
/// immediats : l'editeur y restait ouvert, boutons desactives, et Cancel /// immediats : l'editeur y restait ouvert, boutons desactives, et Cancel
/// lui-meme ne repondait plus. /// lui-meme ne repondait plus.
-65
View File
@@ -196,7 +196,6 @@ type
{$IFDEF DEBUG} {$IFDEF DEBUG}
procedure CryptoSelfCheck; procedure CryptoSelfCheck;
procedure PatchSelfCheck; procedure PatchSelfCheck;
procedure WriteTest;
{$ENDIF} {$ENDIF}
procedure OpenEntry(AVaultIndex: Integer); procedure OpenEntry(AVaultIndex: Integer);
procedure SaveEntryPassword(const ANewValue: string); procedure SaveEntryPassword(const ANewValue: string);
@@ -302,7 +301,6 @@ begin
ShowUnlock; ShowUnlock;
{$IFDEF DEBUG} {$IFDEF DEBUG}
WebCall('PM.setDebug(true)');
// VERIFICATION DU CHIFFREMENT AVANT TOUTE ECRITURE REELLE (§4). Elle depose // VERIFICATION DU CHIFFREMENT AVANT TOUTE ECRITURE REELLE (§4). Elle depose
// aussi un conteneur sur le disque prive, pour qu'il soit relu par l'AUTRE // aussi un conteneur sur le disque prive, pour qu'il soit relu par l'AUTRE
// implementation : un aller-retour avec soi-meme ne prouve que la coherence // implementation : un aller-retour avec soi-meme ne prouve que la coherence
@@ -708,65 +706,6 @@ begin
end).Start; end).Start;
end; end;
/// Essai du chemin d'ecriture, VERS UN AUTRE FICHIER que le coffre.
///
/// Le premier PUT est le moment ou une erreur de transport se paie en donnees
/// perdues : un corps mal forme, un sha mal place, et vault-sync.json est
/// remplace par quelque chose que personne ne sait relire. L'ecrire d'abord
/// sous un autre nom prouve exactement les memes choses - enveloppe, jeton,
/// droits, sha rendu - sans mettre le coffre en jeu une seule fois.
procedure TForm8.WriteTest;
var
LUrl: string;
begin
if FBusy then Exit;
SetBusy(True);
// Meme depot, autre fichier : on remplace le dernier segment du chemin.
LUrl := FUrl.Substring(0, FUrl.LastIndexOf('/') + 1) + 'write-test.json';
TThread.CreateAnonymousThread(
procedure
var
LClient: TSyncClient;
LRes, LGet: TSyncResult;
LBody, LSha: string;
begin
LClient := TSyncClient.Create(LUrl, FToken);
try
LBody := EncryptContainer(
Format('{"probe":"write path","at":"%s"}',
[FormatDateTime('yyyy-mm-dd"T"hh:nn:ss"Z"',
TTimeZone.Local.ToUniversalTime(Now))]),
FSyncPwd, 1000);
// LIRE D'ABORD, ECRIRE ENSUITE - la sequence reelle. Un sha vide veut
// dire « cree », et Gitea refuse alors en 422 si le fichier est deja
// la ; c'est le cas de mise a jour qui compte, et il exige le sha
// courant. Un 404 au GET signifie simplement qu'il n'existe pas
// encore : on cree, sha vide.
LGet := LClient.Fetch;
if LGet.Status = ssOK then
LSha := LGet.ETag
else
LSha := '';
Log(Format('GET first: HTTP %d, sha %s', [LGet.HttpCode,
IfThen(LSha = '', '(aucun - creation)', LSha)]));
Log('PUT ' + LUrl);
Log(Format(' token %s (%d chars), body %d chars',
[Fingerprint(FToken), Length(FToken), Length(LBody)]));
LRes := LClient.Put(LBody, LSha, 'write path probe from phone');
case LRes.Status of
ssOK: Log(' OK ' + LRes.HttpCode.ToString + ' - new sha ' + LRes.ETag);
ssConflict: Log(' 409 - file already there, concurrency control works');
else
Log(' failed: ' + LRes.HttpCode.ToString + ' ' + LRes.Error);
end;
finally
LClient.Free;
TThread.Queue(nil, procedure begin SetBusy(False); end);
end;
end).Start;
end;
/// Descend les coordonnees du serveur dans les champs de la page. /// Descend les coordonnees du serveur dans les champs de la page.
procedure TForm8.PushConfig; procedure TForm8.PushConfig;
var var
@@ -896,10 +835,6 @@ begin
// porte est detruite, et HandleWebCommand ne journalise que l'action. // porte est detruite, et HandleWebCommand ne journalise que l'action.
else if LAction = 'setpwd' then else if LAction = 'setpwd' then
SaveEntryPassword(TNetEncoding.URL.Decode(Param('v'))) SaveEntryPassword(TNetEncoding.URL.Decode(Param('v')))
{$IFDEF DEBUG}
else if LAction = 'writetest' then
WriteTest
{$ENDIF}
else if LAction = 'copy' then else if LAction = 'copy' then
begin begin