feat(android): an entry can be deleted from the phone

Both, or nothing: the row leaves the snapshot AND a tombstone takes its
place. Removing the row alone does not delete - the desktop still holds its
own copy, and the next pull would bring it back as an entry we never had.
The time matters too: the desktop's arbitration keeps an entry edited AFTER
the deletion rather than killing it a second time.

No trash on the phone, deliberately. The desktop's is a `deleted` flag that
does not travel in sync, so a deletion stopping there would propagate
nowhere. Hence a button at the very bottom, in two taps, that disarms itself
after four seconds - a button left armed gets touched by accident much
later, and this is the one action with no way back.

The confirmation is in the page because the engine cannot offer one: a
JavaScript prompt needs a WebChromeClient FMX does not provide.

Not exercised on the device: it would have destroyed a real entry.
This commit is contained in:
r-zakarya
2026-08-21 16:21:45 +01:00
parent 7a0ae6398b
commit 5107ebf7b2
4 changed files with 140 additions and 1 deletions
+33
View File
@@ -242,6 +242,8 @@ type
/// Ajoute une entree au coffre. Le mot de passe voyage par le pont,
/// comme 'setfield' - meme regle, meme journalisation muette.
procedure CreateEntry(const ATitle, ASite, AUser, APwd, AKind: string);
/// Supprime l'entree ouverte, pierre tombale comprise. Sans retour.
procedure DeleteCurrentEntry;
procedure ReopenEntryByUuid(const AUuid: string);
procedure OnClipTimer(Sender: TObject);
procedure OnDeadline(Sender: TObject);
@@ -901,6 +903,34 @@ begin
end);
end;
/// SUPPRIMER - la seule action sans retour depuis le telephone.
///
/// Elle ne met pas a la corbeille : celle du poste ne voyage pas dans la
/// synchronisation, donc s'y arreter ne supprimerait nulle part. Elle retire la
/// ligne ET pose la pierre tombale, ce qui fait disparaitre l'entree du poste
/// au prochain tirage.
///
/// La page demande confirmation avant d'en arriver ici.
procedure TForm8.DeleteCurrentEntry;
var
LUuid, LTitle: string;
begin
if FCurrent.Uuid = '' then
begin
RefuseChange('This entry has no uuid - cannot be deleted');
Exit;
end;
LUuid := FCurrent.Uuid;
LTitle := FCurrent.Display;
// Pas de rang a rouvrir : l'entree n'existera plus. AdoptVault ramene la
// liste, ce qui est exactement ou l'on veut se retrouver.
CommitChange('Deleted: ' + LTitle, 'entry deleted from phone', '',
function (APlain: string): string
begin
Result := DeleteEntry(APlain, LUuid);
end);
end;
procedure TForm8.CreateEntry(const ATitle, ASite, AUser, APwd, AKind: string);
begin
// Une entree sans rien pour la reconnaitre serait une ligne muette dans la
@@ -1061,6 +1091,9 @@ begin
TNetEncoding.URL.Decode(Param('p')),
TNetEncoding.URL.Decode(Param('k')))
else if LAction = 'delentry' then
DeleteCurrentEntry
else if LAction = 'setfield' then
SaveEntryField(TNetEncoding.URL.Decode(Param('f')),
TNetEncoding.URL.Decode(Param('v')))