feat(android): entries can be created on the phone
A vault you cannot add to is not a vault, so an account created here was useless without this. A plus button next to the padlock, a form, and a kind - login or note, which the vault distinguishes and whose body is the password field either way. One save path now, two transports, and the rule that decides between them is: if a local container exists it is the authority. A vault born here has no repository; a vault pulled from one has no local container. The case where both exist - a local vault that later gains a repository - needs a merge, and that is section 7 step 4. The patch arrives as a function rather than an already-patched snapshot: the remote path must modify WHAT IT HAS JUST PULLED, or it would overwrite whatever another device published in between. That constraint is why the two transports could not simply share a finished payload. Two traps closed while passing: the form left the typed password sitting in the document after the save, and a refusal left its Save button dead with the text behind it. Verified on the device that the button and the form appear and read correctly. Saving was not exercised - the vault on this phone syncs to a real repository, and a test entry would have been published to it with no way to delete it from here yet.
This commit is contained in:
@@ -40,6 +40,17 @@ function PatchEntryField(const APlainJson, AUuid, AField, AValue: string): strin
|
||||
/// naisse pas dans un dialecte a part.
|
||||
function NewEmptySnapshot(const AUsername: string): string;
|
||||
|
||||
/// Ajoute une entree a l'instantane et rend le JSON complet.
|
||||
///
|
||||
/// LA MEME FORME QUE CE QU'ECRIT LE POSTE, cles vides comprises : une entree
|
||||
/// nee ici sera relue la-bas, et une cle absente ne se comporte pas toujours
|
||||
/// comme une cle vide. `folder` vaut 'All' parce que c'est le dossier par
|
||||
/// defaut du poste, pas parce que c'est joli.
|
||||
///
|
||||
/// Un uuid neuf, et lui seul : pas de tombstone, on ne supprime pas encore.
|
||||
function AddEntry(const APlainJson, ATitle, ASite, AUsername, APassword,
|
||||
AKind: string): string;
|
||||
|
||||
/// Horodatage UTC au format que tout le reste emploie (toISOString cote JS).
|
||||
/// MELANGER heure locale et UTC a deja decale l'arbitrage de la fusion une
|
||||
/// fois : il n'y a qu'une source d'heure ici, et elle est en UTC.
|
||||
@@ -75,6 +86,50 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function AddEntry(const APlainJson, ATitle, ASite, AUsername, APassword,
|
||||
AKind: string): string;
|
||||
var
|
||||
LRoot, LEntry: TJSONObject;
|
||||
LEntries: TJSONArray;
|
||||
LNow: string;
|
||||
begin
|
||||
LRoot := TJSONObject.ParseJSONValue(APlainJson) as TJSONObject;
|
||||
if LRoot = nil then
|
||||
raise EPMAPatch.Create('Snapshot is not a JSON object');
|
||||
try
|
||||
LEntries := LRoot.GetValue('entries') as TJSONArray;
|
||||
if LEntries = nil then
|
||||
raise EPMAPatch.Create('Snapshot has no entries array');
|
||||
|
||||
LNow := NowUtcIso;
|
||||
LEntry := TJSONObject.Create;
|
||||
// Minuscules et sans accolades : la forme que tout le reste emploie.
|
||||
LEntry.AddPair('uuid', LowerCase(Copy(GUIDToString(TGUID.NewGuid), 2, 36)));
|
||||
LEntry.AddPair('title', ATitle);
|
||||
LEntry.AddPair('site', ASite);
|
||||
LEntry.AddPair('username', AUsername);
|
||||
// Le corps d'une note EST son mot de passe cote stockage - il n'y a pas de
|
||||
// colonne separee, ni ici ni sur le poste.
|
||||
LEntry.AddPair('password', APassword);
|
||||
LEntry.AddPair('folder', 'All');
|
||||
LEntry.AddPair('tags', TJSONArray.Create);
|
||||
LEntry.AddPair('totp_secret', '');
|
||||
LEntry.AddPair('kind', AKind);
|
||||
LEntry.AddPair('template', '');
|
||||
LEntry.AddPair('custom_fields', TJSONArray.Create);
|
||||
LEntry.AddPair('attachments', TJSONArray.Create);
|
||||
LEntry.AddPair('icon_b64', '');
|
||||
LEntry.AddPair('icon_custom', TJSONNumber.Create(0));
|
||||
LEntry.AddPair('created_at', LNow);
|
||||
LEntry.AddPair('updated_at', LNow);
|
||||
LEntries.AddElement(LEntry);
|
||||
|
||||
Result := LRoot.ToJSON;
|
||||
finally
|
||||
LRoot.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function PatchEntryField(const APlainJson, AUuid, AField, AValue: string): string;
|
||||
var
|
||||
LRoot, LEntry: TJSONObject;
|
||||
|
||||
Reference in New Issue
Block a user