feat(android): every field of an entry can be changed, not just the password
PatchEntryField already took a field name; what was missing was carrying it from the row. It travels in the detail row itself rather than being guessed from the label - a caption is prose, and the day it changes the page would send a field name that does not exist, or quietly stop offering the edit. Delphi does not take it on trust either: four names accepted, the rest refused. The snapshot also carries tombstones, attachments and timestamps that PatchEntryField would overwrite without understanding them. An empty row now shows IF it can be filled - a missing username you cannot add is a dead end. Emptying is allowed, except on a password, where it would almost always be a slip; the refusal says so rather than doing nothing. Show only appears on a masked field: on a site in clear it has nothing to reveal, and would in fact hide it. A note keeps only Name and Content - a site and a username mean nothing there.
This commit is contained in:
@@ -149,14 +149,17 @@ type
|
||||
TDetailRow = record
|
||||
Caption, Value: string;
|
||||
Secret: Boolean;
|
||||
/// Modifiable depuis le telephone. UN SEUL champ l'est pour l'instant - le
|
||||
/// mot de passe - et c'est deliberé : creer ou supprimer une entree
|
||||
/// demanderait des tombstones et la generation d'uuid, alors que changer un
|
||||
/// mot de passe couvre l'essentiel du besoin sans rien de tout cela.
|
||||
/// Marque ICI plutot que reconnu au libelle cote page : un intitule est de
|
||||
/// la prose, et le jour ou il change la page cesserait silencieusement de
|
||||
/// proposer la modification.
|
||||
Editable: Boolean;
|
||||
/// LE NOM DU CHAMP DANS L'INSTANTANE, vide si la ligne ne se modifie pas.
|
||||
///
|
||||
/// Il porte les deux choses a la fois : qu'on puisse editer, et quoi
|
||||
/// editer. Marque ICI plutot que devine du libelle cote page : un intitule
|
||||
/// est de la prose, et le jour ou il change la page enverrait un nom de
|
||||
/// champ inexistant - ou, pire, cesserait silencieusement de proposer la
|
||||
/// modification.
|
||||
///
|
||||
/// Supprimer une entree n'y figure pas : cela demande des tombstones, et
|
||||
/// c'est l'etape 3 de la §7.
|
||||
Field: string;
|
||||
end;
|
||||
|
||||
function BuildRows(const E: TVaultEntry): TArray<TDetailRow>;
|
||||
@@ -166,26 +169,33 @@ var
|
||||
LMeta: string;
|
||||
|
||||
procedure Add(const ACaption, AValue: string; ASecret: Boolean;
|
||||
AEditable: Boolean = False);
|
||||
const AField: string = '');
|
||||
begin
|
||||
if AValue = '' then Exit; // une ligne vide n'apprend rien
|
||||
// Une ligne vide n'apprend rien - SAUF si elle se remplit. Un identifiant
|
||||
// absent qu'on ne peut pas ajouter serait une impasse.
|
||||
if (AValue = '') and (AField = '') then Exit;
|
||||
SetLength(LRows, Length(LRows) + 1);
|
||||
LRows[High(LRows)].Caption := ACaption;
|
||||
LRows[High(LRows)].Value := AValue;
|
||||
LRows[High(LRows)].Secret := ASecret;
|
||||
LRows[High(LRows)].Editable := AEditable;
|
||||
LRows[High(LRows)].Caption := ACaption;
|
||||
LRows[High(LRows)].Value := AValue;
|
||||
LRows[High(LRows)].Secret := ASecret;
|
||||
LRows[High(LRows)].Field := AField;
|
||||
end;
|
||||
|
||||
begin
|
||||
Add('Site', E.Site, False);
|
||||
Add('Username', E.Username, False);
|
||||
Add('Name', E.Title, False, 'title');
|
||||
// Le corps d'une note est range dans le champ mot de passe : meme donnee,
|
||||
// autre intitule, et pas de raison de la masquer comme un secret.
|
||||
// Seule la ligne du mot de passe est modifiable. Le corps d'une note le
|
||||
// serait en principe, mais il se saisit sur plusieurs lignes - un autre
|
||||
// controle, une autre fois.
|
||||
if E.IsNote then Add('Content', E.Password, False)
|
||||
else Add('Password', E.Password, True, True);
|
||||
//
|
||||
// Un site et un identifiant n'ont aucun sens pour une note. Les proposer
|
||||
// vides reviendrait a inviter a remplir ce que rien n'affichera jamais.
|
||||
if E.IsNote then
|
||||
Add('Content', E.Password, False, 'password')
|
||||
else
|
||||
begin
|
||||
Add('Site', E.Site, False, 'site');
|
||||
Add('Username', E.Username, False, 'username');
|
||||
Add('Password', E.Password, True, 'password');
|
||||
end;
|
||||
|
||||
for I := 0 to High(E.Fields) do
|
||||
Add(E.Fields[I].Caption, E.Fields[I].Value, E.Fields[I].Secret);
|
||||
@@ -233,7 +243,8 @@ begin
|
||||
LObj.AddPair('c', LRows[I].Caption);
|
||||
LObj.AddPair('v', LRows[I].Value);
|
||||
LObj.AddPair('s', TJSONBool.Create(LRows[I].Secret));
|
||||
if LRows[I].Editable then LObj.AddPair('e', TJSONBool.Create(True));
|
||||
// 'e' porte le nom du champ, et sa presence vaut "modifiable".
|
||||
if LRows[I].Field <> '' then LObj.AddPair('e', LRows[I].Field);
|
||||
LArr.AddElement(LObj);
|
||||
end;
|
||||
Result := LRoot.ToJSON;
|
||||
|
||||
Reference in New Issue
Block a user